This is an automated email from the ASF dual-hosted git repository. ntimofeev pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/cayenne.git
The following commit(s) were added to refs/heads/master by this push: new 2ff64413d Revert "CAY-2876 Memory leak in the ObjectStore" 2ff64413d is described below commit 2ff64413d4a7c7d1f98ac59f74770e166545ed21 Author: Nikita Timofeev <stari...@gmail.com> AuthorDate: Mon Mar 10 16:13:34 2025 +0400 Revert "CAY-2876 Memory leak in the ObjectStore" This reverts commit bf344662776351b64deb1fdf0bbe0676db86c92e. --- RELEASE-NOTES.txt | 1 - .../commitlog/CommitLogFilter_All_FlattenedIT.java | 10 +- .../access/DefaultObjectMapRetainStrategy.java | 4 +- .../apache/cayenne/access/NoSyncObjectStore.java | 4 +- .../java/org/apache/cayenne/access/ObjectDiff.java | 43 ++--- .../cayenne/access/ObjectMapRetainStrategy.java | 4 +- .../org/apache/cayenne/access/ObjectStore.java | 195 ++++++++++----------- .../apache/cayenne/access/ObjectStoreEntry.java | 70 -------- .../cayenne/access/ObjectStoreGraphDiff.java | 7 +- .../access/DataContextPrefetchMultistepIT.java | 2 +- .../cayenne/access/DataContextSerializationIT.java | 4 +- .../org/apache/cayenne/access/ObjectStoreTest.java | 2 +- 12 files changed, 127 insertions(+), 219 deletions(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 52ab66e33..f964462b3 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -21,7 +21,6 @@ Bug Fixes: CAY-2701 MySQL DST-related LocalDateTime issues CAY-2871 QualifierTranslator breaks on a relationship with a compound FK CAY-2872 CayenneModeler "Documentation" link is broken -CAY-2876 Memory leak in the ObjectStore CAY-2879 Negative number for non parameterized ObjectSelect query not processed correctly ---------------------------------- diff --git a/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/CommitLogFilter_All_FlattenedIT.java b/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/CommitLogFilter_All_FlattenedIT.java index 63c0f1a6e..7a755890a 100644 --- a/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/CommitLogFilter_All_FlattenedIT.java +++ b/cayenne-commitlog/src/test/java/org/apache/cayenne/commitlog/CommitLogFilter_All_FlattenedIT.java @@ -27,8 +27,6 @@ import org.apache.cayenne.commitlog.model.ObjectChange; import org.apache.cayenne.commitlog.model.ObjectChangeType; import org.apache.cayenne.commitlog.model.ToManyRelationshipChange; import org.apache.cayenne.commitlog.unit.FlattenedRuntimeCase; -import org.apache.cayenne.configuration.Constants; -import org.apache.cayenne.configuration.runtime.CoreModule; import org.apache.cayenne.query.SelectById; import org.apache.cayenne.runtime.CayenneRuntimeBuilder; import org.junit.Before; @@ -50,8 +48,6 @@ public class CommitLogFilter_All_FlattenedIT extends FlattenedRuntimeCase { protected CayenneRuntimeBuilder configureCayenne() { this.mockListener = mock(CommitLogListener.class); return super.configureCayenne() - .addModule(b -> CoreModule.extend(b) - .setProperty(Constants.OBJECT_RETAIN_STRATEGY_PROPERTY, "soft")) .addModule(b -> CommitLogModule.extend(b).addListener(mockListener)); } @@ -67,9 +63,9 @@ public class CommitLogFilter_All_FlattenedIT extends FlattenedRuntimeCase { e4.insert(12); e34.insert(1, 11); - E3 e3 = SelectById.queryId(E3.class, 1).selectOne(context); - E4 e4_1 = SelectById.queryId(E4.class, 11).selectOne(context); - E4 e4_2 = SelectById.queryId(E4.class, 12).selectOne(context); + E3 e3 = SelectById.query(E3.class, 1).selectOne(context); + E4 e4_1 = SelectById.query(E4.class, 11).selectOne(context); + E4 e4_2 = SelectById.query(E4.class, 12).selectOne(context); doAnswer((Answer<Object>) invocation -> { diff --git a/cayenne/src/main/java/org/apache/cayenne/access/DefaultObjectMapRetainStrategy.java b/cayenne/src/main/java/org/apache/cayenne/access/DefaultObjectMapRetainStrategy.java index 8fcbf42db..05f248ed8 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/DefaultObjectMapRetainStrategy.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/DefaultObjectMapRetainStrategy.java @@ -22,7 +22,7 @@ import java.util.HashMap; import java.util.Map; import org.apache.cayenne.CayenneRuntimeException; -import org.apache.cayenne.ObjectId; +import org.apache.cayenne.Persistent; import org.apache.cayenne.configuration.Constants; import org.apache.cayenne.configuration.RuntimeProperties; import org.apache.cayenne.di.Inject; @@ -46,7 +46,7 @@ public class DefaultObjectMapRetainStrategy implements ObjectMapRetainStrategy { this.runtimeProperties = runtimeProperties; } - public Map<ObjectId, ObjectStoreEntry> createObjectMap() { + public Map<Object, Persistent> createObjectMap() { String strategy = runtimeProperties.get(Constants.OBJECT_RETAIN_STRATEGY_PROPERTY); if (strategy == null || WEAK_RETAIN_STRATEGY.equals(strategy)) { diff --git a/cayenne/src/main/java/org/apache/cayenne/access/NoSyncObjectStore.java b/cayenne/src/main/java/org/apache/cayenne/access/NoSyncObjectStore.java index 914543a23..9358c08d2 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/NoSyncObjectStore.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/NoSyncObjectStore.java @@ -20,7 +20,7 @@ package org.apache.cayenne.access; import java.util.Map; -import org.apache.cayenne.ObjectId; +import org.apache.cayenne.Persistent; /** * An {@link ObjectStore} which doesn't receive notifications @@ -30,7 +30,7 @@ import org.apache.cayenne.ObjectId; */ public class NoSyncObjectStore extends ObjectStore { - public NoSyncObjectStore(DataRowStore dataRowCache, Map<ObjectId, ObjectStoreEntry> objectMap) { + public NoSyncObjectStore(DataRowStore dataRowCache, Map<Object, Persistent> objectMap) { super(dataRowCache, objectMap); } diff --git a/cayenne/src/main/java/org/apache/cayenne/access/ObjectDiff.java b/cayenne/src/main/java/org/apache/cayenne/access/ObjectDiff.java index 6ae2df6d3..9b2b68de4 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/ObjectDiff.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/ObjectDiff.java @@ -65,22 +65,23 @@ public class ObjectDiff extends NodeDiff { private Map<ArcOperation, ArcOperation> flatIds; private Map<ArcOperation, ArcOperation> phantomFks; - private final ObjectStoreEntry entry; + private Persistent object; - ObjectDiff(final ObjectStoreEntry entry) { + ObjectDiff(final Persistent object) { - super(entry.persistent().getObjectId()); + super(object.getObjectId()); - // retain the object, as ObjectStore may have weak references to registered objects, - // and we can't allow it to deallocate dirty objects. - this.entry = entry; + // retain the object, as ObjectStore may have weak references to + // registered + // objects and we can't allow it to deallocate dirty objects. + this.object = object; - EntityResolver entityResolver = object().getObjectContext().getEntityResolver(); + EntityResolver entityResolver = object.getObjectContext().getEntityResolver(); - this.entityName = object().getObjectId().getEntityName(); + this.entityName = object.getObjectId().getEntityName(); this.classDescriptor = entityResolver.getClassDescriptor(entityName); - int state = object().getPersistenceState(); + int state = object.getPersistenceState(); // take snapshot of simple properties and arcs used for optimistic // locking.. @@ -98,7 +99,7 @@ public class ObjectDiff extends NodeDiff { @Override public boolean visitAttribute(AttributeProperty property) { - snapshot.put(property.getName(), property.readProperty(object())); + snapshot.put(property.getName(), property.readProperty(object)); return true; } @@ -113,8 +114,8 @@ public class ObjectDiff extends NodeDiff { // eagerly resolve optimistically locked relationships Object target = (lock && isUsedForLocking) - ? property.readProperty(object()) - : property.readPropertyDirectly(object()); + ? property.readProperty(object) + : property.readPropertyDirectly(object); if (target instanceof Persistent) { target = ((Persistent) target).getObjectId(); @@ -128,15 +129,15 @@ public class ObjectDiff extends NodeDiff { } } - Persistent object() { - return entry.persistent(); + Object getObject() { + return object; } ClassDescriptor getClassDescriptor() { // class descriptor is initiated in constructor, but is nullified on // serialization if (classDescriptor == null) { - EntityResolver entityResolver = object().getObjectContext().getEntityResolver(); + EntityResolver entityResolver = object.getObjectContext().getEntityResolver(); this.classDescriptor = entityResolver.getClassDescriptor(entityName); } @@ -151,7 +152,7 @@ public class ObjectDiff extends NodeDiff { Object value = arcSnapshot != null ? arcSnapshot.get(propertyName) : null; if (value instanceof Fault) { - Persistent target = (Persistent) ((Fault) value).resolveFault(object(), propertyName); + Persistent target = (Persistent) ((Fault) value).resolveFault(object, propertyName); value = target != null ? target.getObjectId() : null; arcSnapshot.put(propertyName, value); @@ -166,7 +167,7 @@ public class ObjectDiff extends NodeDiff { public ObjectId getCurrentArcSnapshotValue(String propertyName) { Object value = currentArcSnapshot != null ? currentArcSnapshot.get(propertyName) : null; if (value instanceof Fault) { - Persistent target = (Persistent) ((Fault) value).resolveFault(object(), propertyName); + Persistent target = (Persistent) ((Fault) value).resolveFault(object, propertyName); value = target != null ? target.getObjectId() : null; currentArcSnapshot.put(propertyName, value); @@ -327,7 +328,7 @@ public class ObjectDiff extends NodeDiff { return false; } - int state = object().getPersistenceState(); + int state = object.getPersistenceState(); if (state == PersistenceState.NEW || state == PersistenceState.DELETED) { return false; } @@ -341,7 +342,7 @@ public class ObjectDiff extends NodeDiff { public boolean visitAttribute(AttributeProperty property) { Object oldValue = snapshot.get(property.getName()); - Object newValue = property.readProperty(object()); + Object newValue = property.readProperty(object); if (!property.equals(oldValue, newValue)) { modFound[0] = true; @@ -362,7 +363,7 @@ public class ObjectDiff extends NodeDiff { return true; } - Object newValue = property.readPropertyDirectly(object()); + Object newValue = property.readPropertyDirectly(object); if (newValue instanceof Fault) { return true; } @@ -410,7 +411,7 @@ public class ObjectDiff extends NodeDiff { @Override public boolean visitAttribute(AttributeProperty property) { - Object newValue = property.readProperty(object()); + Object newValue = property.readProperty(object); // no baseline to compare if (snapshot == null) { diff --git a/cayenne/src/main/java/org/apache/cayenne/access/ObjectMapRetainStrategy.java b/cayenne/src/main/java/org/apache/cayenne/access/ObjectMapRetainStrategy.java index 3749298d0..8d1e4fbb9 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/ObjectMapRetainStrategy.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/ObjectMapRetainStrategy.java @@ -20,7 +20,7 @@ package org.apache.cayenne.access; import java.util.Map; -import org.apache.cayenne.ObjectId; +import org.apache.cayenne.Persistent; /** * A strategy for retaining objects in {@link ObjectStore}. The strategy can be weak, soft @@ -30,5 +30,5 @@ import org.apache.cayenne.ObjectId; */ public interface ObjectMapRetainStrategy { - Map<ObjectId, ObjectStoreEntry> createObjectMap(); + Map<Object, Persistent> createObjectMap(); } diff --git a/cayenne/src/main/java/org/apache/cayenne/access/ObjectStore.java b/cayenne/src/main/java/org/apache/cayenne/access/ObjectStore.java index ff7f2facd..0dcdc7aa1 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/ObjectStore.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/ObjectStore.java @@ -53,7 +53,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.ConcurrentHashMap; /** * ObjectStore stores objects using their ObjectId as a key. It works as a dedicated @@ -65,16 +65,14 @@ import java.util.concurrent.atomic.AtomicInteger; */ public class ObjectStore implements Serializable, SnapshotEventListener, GraphManager { - protected Map<ObjectId, ObjectStoreEntry> objectMap; + protected Map<Object, Persistent> objectMap; protected Map<Object, ObjectDiff> changes; /** * Map that tracks flattened paths for given object Id that is present in db. * Presence of path in this map is used to separate insert from update case of flattened records. * @since 4.1 - * @deprecated since 5.0 unused */ - @Deprecated protected Map<Object, Map<CayennePath, ObjectId>> trackedFlattenedPaths; // a sequential id used to tag GraphDiffs so that they can later be sorted in the @@ -108,7 +106,7 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa * * @since 3.0 */ - public ObjectStore(DataRowStore dataRowCache, Map<ObjectId, ObjectStoreEntry> objectMap) { + public ObjectStore(DataRowStore dataRowCache, Map<Object, Persistent> objectMap) { setDataRowCache(dataRowCache); if (objectMap != null) { this.objectMap = objectMap; @@ -139,7 +137,7 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa Collection<GraphDiff> getLifecycleEventInducedChanges() { return lifecycleEventInducedChanges != null ? lifecycleEventInducedChanges - : Collections.emptyList(); + : Collections.<GraphDiff>emptyList(); } void registerLifecycleEventInducedChange(GraphDiff diff) { @@ -167,13 +165,11 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa if (objectDiff == null) { - ObjectStoreEntry entry = objectMap.get((ObjectId)nodeId); - if (entry == null || !entry.hasObject()) { + Persistent object = objectMap.get(nodeId); + if (object == null) { throw new CayenneRuntimeException("No object is registered in context with Id %s", nodeId); } - Persistent object = entry.persistent(); - if (object.getPersistenceState() == PersistenceState.COMMITTED) { object.setPersistenceState(PersistenceState.MODIFIED); @@ -200,7 +196,7 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa } } - objectDiff = new ObjectDiff(entry); + objectDiff = new ObjectDiff(object); objectDiff.setDiffId(++currentDiffId); changes.put(nodeId, objectDiff); } @@ -218,13 +214,7 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa * @since 1.2 */ public int registeredObjectsCount() { - AtomicInteger counter = new AtomicInteger(); - objectMap.forEach((id, obj) -> { - if(obj.hasObject()){ - counter.incrementAndGet(); - } - }); - return counter.get(); + return objectMap.size(); } /** @@ -293,7 +283,7 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa */ // this method is exactly the same as "objectsInvalidated", only additionally it // throws out registered objects - public synchronized void objectsUnregistered(Collection<?> objects) { + public synchronized void objectsUnregistered(Collection objects) { if (objects.isEmpty()) { return; } @@ -308,6 +298,9 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa // remove object but not snapshot objectMap.remove(id); changes.remove(id); + if(id != null && trackedFlattenedPaths != null) { + trackedFlattenedPaths.remove(id); + } ids.add(id); object.setObjectContext(null); @@ -321,10 +314,10 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa // send an event for removed snapshots getDataRowCache().processSnapshotChanges( this, - Collections.emptyMap(), - Collections.emptyList(), + Collections.<ObjectId, DataRow>emptyMap(), + Collections.<ObjectId>emptyList(), ids, - Collections.emptyList()); + Collections.<ObjectId>emptyList()); } } @@ -393,11 +386,11 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa for (Object id : changes.keySet()) { - ObjectStoreEntry object = objectMap.get((ObjectId)id); + Persistent object = objectMap.get(id); // assume that no new or deleted objects are present (as otherwise commit // wouldn't have been phantom). - object.persistent().setPersistenceState(PersistenceState.COMMITTED); + object.setPersistenceState(PersistenceState.COMMITTED); } // clear caches @@ -412,10 +405,8 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa public void postprocessAfterCommit(GraphDiff parentChanges) { // scan through changed objects, set persistence state to committed - for (Object key : changes.keySet()) { - ObjectId id = (ObjectId) key; - // persistent object for the diff should always exist - Persistent object = objectMap.get(id).persistent(); + for (Object id : changes.keySet()) { + Persistent object = objectMap.get(id); switch (object.getPersistenceState()) { case PersistenceState.DELETED: @@ -512,7 +503,7 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa * Returns an iterator over the registered objects. */ public synchronized Iterator<Persistent> getObjectIterator() { - return new EntryIterator(objectMap.values().iterator()); + return objectMap.values().iterator(); } /** @@ -533,9 +524,9 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa public synchronized List<Persistent> objectsInState(int state) { List<Persistent> filteredObjects = new ArrayList<>(); - for (ObjectStoreEntry entry : objectMap.values()) { - if (entry.hasObject() && entry.persistent().getPersistenceState() == state) { - filteredObjects.add(entry.persistent()); + for (Persistent object : objectMap.values()) { + if (object.getPersistenceState() == state) { + filteredObjects.add(object); } } @@ -595,21 +586,11 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa } void processIdChange(Object nodeId, Object newId) { - ObjectStoreEntry entry = objectMap.remove((ObjectId)nodeId); - - if (entry != null) { - ObjectId id = (ObjectId) newId; - entry.persistent().setObjectId(id); - objectMap.merge(id, entry, (oldValue, newValue) -> { - if(oldValue.trackedFlattenedPaths != null) { - if(newValue.trackedFlattenedPaths != null) { - newValue.trackedFlattenedPaths.putAll(oldValue.trackedFlattenedPaths); - } else { - newValue.trackedFlattenedPaths = oldValue.trackedFlattenedPaths; - } - } - return newValue; - }); + Persistent object = objectMap.remove(nodeId); + + if (object != null) { + object.setObjectId((ObjectId) newId); + objectMap.put(newId, object); ObjectDiff change = changes.remove(nodeId); if (change != null) { @@ -617,6 +598,12 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa } } + if(trackedFlattenedPaths != null) { + Map<CayennePath, ObjectId> paths = trackedFlattenedPaths.remove(nodeId); + if(paths != null) { + trackedFlattenedPaths.put(newId, paths); + } + } } /** @@ -627,10 +614,9 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa void processDeletedID(ObjectId nodeId) { // access object map directly - the method should be called in a synchronized context... - ObjectStoreEntry entry = objectMap.get(nodeId); + Persistent object = objectMap.get(nodeId); - if (entry != null && entry.hasObject()) { - Persistent object = entry.persistent(); + if (object != null) { DataContextDelegate delegate; switch (object.getPersistenceState()) { @@ -726,14 +712,11 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa void processIndirectlyModifiedIDs(Collection<ObjectId> indirectlyModifiedIDs) { for (ObjectId oid : indirectlyModifiedIDs) { // access object map directly - the method should be called in a synchronized context... - ObjectStoreEntry entry = objectMap.get(oid); + final Persistent object = objectMap.get(oid); - if (entry == null - || !entry.hasObject() - || entry.persistent().getPersistenceState() != PersistenceState.COMMITTED) { + if (object == null || object.getPersistenceState() != PersistenceState.COMMITTED) { continue; } - Persistent object = entry.persistent(); // for now break all "independent" object relationships... // in the future we may want to be more precise and go after modified @@ -783,12 +766,11 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa void processUpdatedSnapshot(ObjectId nodeId, DataRow diff) { // access object map directly - the method should be called in a synchronized context... - ObjectStoreEntry entry = objectMap.get(nodeId); + Persistent object = objectMap.get(nodeId); // no object, or HOLLOW object require no processing - if (entry != null && entry.hasObject()) { + if (object != null) { - Persistent object = entry.persistent(); int state = object.getPersistenceState(); if (state != PersistenceState.HOLLOW) { @@ -862,12 +844,13 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa * @since 1.2 */ @Override - public synchronized Persistent getNode(Object nodeId) { - ObjectStoreEntry entry = objectMap.get((ObjectId) nodeId); - if(entry == null) { - return null; - } - return entry.persistent(); + public synchronized Object getNode(Object nodeId) { + return objectMap.get(nodeId); + } + + // non-synchronized version of getNode for private use + final Object getNodeNoSync(Object nodeId) { + return objectMap.get(nodeId); } /** @@ -878,13 +861,7 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa */ @Override public synchronized Collection<Object> registeredNodes() { - List<Object> values = new ArrayList<>(objectMap.size()); - objectMap.forEach((id, entry) -> { - if(entry.hasObject()) { - values.add(entry.persistent()); - } - }); - return values; + return new ArrayList<Object>(objectMap.values()); } /** @@ -892,15 +869,15 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa */ @Override public synchronized void registerNode(Object nodeId, Object nodeObject) { - objectMap.put((ObjectId)nodeId, new ObjectStoreEntry((Persistent) nodeObject)); + objectMap.put(nodeId, (Persistent) nodeObject); } /** * @since 1.2 */ @Override - public synchronized Persistent unregisterNode(Object nodeId) { - Persistent object = getNode(nodeId); + public synchronized Object unregisterNode(Object nodeId) { + Object object = getNode(nodeId); if (object != null) { objectsUnregistered(Collections.singleton(object)); } @@ -998,28 +975,52 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa registerDiff(nodeId, diff); } + /** + * Check that flattened path for given object ID has data row in DB. + * @since 4.1 + */ + boolean hasFlattenedPath(ObjectId objectId, CayennePath path) { + if(trackedFlattenedPaths == null) { + return false; + } + return trackedFlattenedPaths + .getOrDefault(objectId, Collections.emptyMap()).containsKey(path); + } + /** * @since 4.2 */ public ObjectId getFlattenedId(ObjectId objectId, CayennePath path) { - ObjectStoreEntry entry = objectMap.get(objectId); - return entry == null ? null : entry.getFlattenedId(path); + if(trackedFlattenedPaths == null) { + return null; + } + + return trackedFlattenedPaths + .getOrDefault(objectId, Collections.emptyMap()).get(path); } /** * @since 4.2 */ public Collection<ObjectId> getFlattenedIds(ObjectId objectId) { - ObjectStoreEntry entry = objectMap.get(objectId); - return entry == null ? null : entry.getFlattenedIds(); + if(trackedFlattenedPaths == null) { + return Collections.emptyList(); + } + + return trackedFlattenedPaths + .getOrDefault(objectId, Collections.emptyMap()).values(); } /** * @since 5.0 */ - public Map<CayennePath, ObjectId> getFlattenedPathIdMap(ObjectId objectId) { - ObjectStoreEntry entry = objectMap.get(objectId); - return entry == null ? null : entry.getFlattenedPathIdMap(); + public Map<CayennePath,ObjectId> getFlattenedPathIdMap(ObjectId objectId) { + if(trackedFlattenedPaths == null) { + return Collections.emptyMap(); + } + + return trackedFlattenedPaths + .getOrDefault(objectId, Collections.emptyMap()); } /** @@ -1027,8 +1028,12 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa * @since 4.1 */ public void markFlattenedPath(ObjectId objectId, CayennePath path, ObjectId id) { - objectMap.computeIfAbsent(objectId, objId -> new ObjectStoreEntry(null)) - .markFlattenedPath(path, id); + if(trackedFlattenedPaths == null) { + trackedFlattenedPaths = new ConcurrentHashMap<>(); + } + trackedFlattenedPaths + .computeIfAbsent(objectId, o -> new ConcurrentHashMap<>()) + .put(path, id); } // an ObjectIdQuery optimized for retrieval of multiple snapshots - it can be reset @@ -1072,28 +1077,4 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa throw new UnsupportedOperationException(); } } - - static class EntryIterator implements Iterator<Persistent> { - - final Iterator<ObjectStoreEntry> iterator; - - EntryIterator(Iterator<ObjectStoreEntry> iterator) { - this.iterator = iterator; - } - - @Override - public boolean hasNext() { - return iterator.hasNext(); - } - - @Override - public Persistent next() { - return iterator.next().persistent(); - } - - @Override - public void remove() { - iterator.remove(); - } - } } diff --git a/cayenne/src/main/java/org/apache/cayenne/access/ObjectStoreEntry.java b/cayenne/src/main/java/org/apache/cayenne/access/ObjectStoreEntry.java deleted file mode 100644 index 8aa0a3261..000000000 --- a/cayenne/src/main/java/org/apache/cayenne/access/ObjectStoreEntry.java +++ /dev/null @@ -1,70 +0,0 @@ -/***************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - ****************************************************************/ - -package org.apache.cayenne.access; - -import org.apache.cayenne.ObjectId; -import org.apache.cayenne.Persistent; -import org.apache.cayenne.exp.path.CayennePath; - -import java.io.Serializable; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @since 5.0 - */ -public class ObjectStoreEntry implements Serializable { - - final protected Persistent persistent; - protected Map<CayennePath, ObjectId> trackedFlattenedPaths; - - public ObjectStoreEntry(Persistent persistent) { - this.persistent = persistent; - } - - public Persistent persistent() { - return persistent; - } - - public boolean hasObject() { - return persistent != null; - } - - public void markFlattenedPath(CayennePath path, ObjectId objectId) { - if (trackedFlattenedPaths == null) { - trackedFlattenedPaths = new ConcurrentHashMap<>(); - } - trackedFlattenedPaths.put(path, objectId); - } - - public ObjectId getFlattenedId(CayennePath path) { - return trackedFlattenedPaths == null ? null : trackedFlattenedPaths.get(path); - } - - public Collection<ObjectId> getFlattenedIds() { - return trackedFlattenedPaths == null ? Collections.emptyList() : trackedFlattenedPaths.values(); - } - - public Map<CayennePath, ObjectId> getFlattenedPathIdMap() { - return trackedFlattenedPaths == null ? Collections.emptyMap() : trackedFlattenedPaths; - } -} diff --git a/cayenne/src/main/java/org/apache/cayenne/access/ObjectStoreGraphDiff.java b/cayenne/src/main/java/org/apache/cayenne/access/ObjectStoreGraphDiff.java index 0208cf5ea..3043fa403 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/ObjectStoreGraphDiff.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/ObjectStoreGraphDiff.java @@ -70,7 +70,8 @@ public class ObjectStoreGraphDiff implements GraphDiff { boolean noop = true; // build a new collection for validation as validation methods may - // result in ObjectStore modifications + // result in + // ObjectStore modifications Collection<Validating> objectsToValidate = null; @@ -80,12 +81,12 @@ public class ObjectStoreGraphDiff implements GraphDiff { noop = false; - if (diff.object() instanceof Validating) { + if (diff.getObject() instanceof Validating) { if (objectsToValidate == null) { objectsToValidate = new ArrayList<>(); } - objectsToValidate.add((Validating) diff.object()); + objectsToValidate.add((Validating) diff.getObject()); } } diff --git a/cayenne/src/test/java/org/apache/cayenne/access/DataContextPrefetchMultistepIT.java b/cayenne/src/test/java/org/apache/cayenne/access/DataContextPrefetchMultistepIT.java index 897d60f53..7bc9eab7b 100644 --- a/cayenne/src/test/java/org/apache/cayenne/access/DataContextPrefetchMultistepIT.java +++ b/cayenne/src/test/java/org/apache/cayenne/access/DataContextPrefetchMultistepIT.java @@ -113,7 +113,7 @@ public class DataContextPrefetchMultistepIT extends RuntimeCase { // get garbage collected, and we won't be able to detect them // so ensure ObjectStore uses a regular map just for this test - context.getObjectStore().objectMap = new HashMap<>(); + context.getObjectStore().objectMap = new HashMap<Object, Persistent>(); // Check the target ArtistExhibit objects do not exist yet diff --git a/cayenne/src/test/java/org/apache/cayenne/access/DataContextSerializationIT.java b/cayenne/src/test/java/org/apache/cayenne/access/DataContextSerializationIT.java index face100de..441a9f8e9 100644 --- a/cayenne/src/test/java/org/apache/cayenne/access/DataContextSerializationIT.java +++ b/cayenne/src/test/java/org/apache/cayenne/access/DataContextSerializationIT.java @@ -21,8 +21,8 @@ package org.apache.cayenne.access; import org.apache.cayenne.Cayenne; import org.apache.cayenne.ObjectContext; -import org.apache.cayenne.ObjectId; import org.apache.cayenne.PersistenceState; +import org.apache.cayenne.Persistent; import org.apache.cayenne.runtime.CayenneRuntime; import org.apache.cayenne.configuration.DefaultRuntimeProperties; import org.apache.cayenne.di.Inject; @@ -152,7 +152,7 @@ public class DataContextSerializationIT extends RuntimeCase { new DefaultRuntimeProperties(domain.getProperties()), domain.getEventManager()); - Map<ObjectId, ObjectStoreEntry> map = new HashMap<>(); + Map<Object, Persistent> map = new HashMap<>(); DataContext localCacheContext = new DataContext(domain, new ObjectStore( snapshotCache, diff --git a/cayenne/src/test/java/org/apache/cayenne/access/ObjectStoreTest.java b/cayenne/src/test/java/org/apache/cayenne/access/ObjectStoreTest.java index b25336196..e79770498 100644 --- a/cayenne/src/test/java/org/apache/cayenne/access/ObjectStoreTest.java +++ b/cayenne/src/test/java/org/apache/cayenne/access/ObjectStoreTest.java @@ -42,7 +42,7 @@ public class ObjectStoreTest { @Before public void before() { DataRowStore sharedCache = mock(DataRowStore.class); - this.objectStore = new ObjectStore(sharedCache, new HashMap<>()); + this.objectStore = new ObjectStore(sharedCache, new HashMap<Object, Persistent>()); } @Test