Author: aadamchik
Date: Tue Nov 21 19:22:16 2006
New Revision: 478018
URL: http://svn.apache.org/viewvc?view=rev&rev=478018
Log:
CAY-709: Leak-free ObjectContext
(first cut - ObjectStore uses weak references to hollow and committed objects)
Added:
incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/test/java/org/apache/cayenne/access/ObjectStoreGCTst.java
Modified:
incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/access/ObjectDiff.java
incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/access/ObjectStore.java
Modified:
incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/access/ObjectDiff.java
URL:
http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/access/ObjectDiff.java?view=diff&rev=478018&r1=478017&r2=478018
==============================================================================
---
incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/access/ObjectDiff.java
(original)
+++
incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/access/ObjectDiff.java
Tue Nov 21 19:22:16 2006
@@ -62,10 +62,16 @@
private Map currentArcSnapshot;
private Map flatIds;
+ Persistent object;
+
ObjectDiff(ObjectStore objectStore, final Persistent object) {
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.object = object;
+
EntityResolver entityResolver =
objectStore.getContext().getEntityResolver();
this.entityName = object.getObjectId().getEntityName();
@@ -326,7 +332,7 @@
getClassDescriptor().visitProperties(new PropertyVisitor() {
public boolean visitAttributeProperty(AttributeProperty property) {
-
+
Object newValue = property.readProperty(object);
// no baseline to compare
Modified:
incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/access/ObjectStore.java
URL:
http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/access/ObjectStore.java?view=diff&rev=478018&r1=478017&r2=478018
==============================================================================
---
incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/access/ObjectStore.java
(original)
+++
incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/access/ObjectStore.java
Tue Nov 21 19:22:16 2006
@@ -56,6 +56,8 @@
import org.apache.cayenne.query.PrefetchTreeNode;
import org.apache.cayenne.query.QueryMetadata;
import org.apache.cayenne.query.RefreshQuery;
+import org.apache.commons.collections.map.AbstractReferenceMap;
+import org.apache.commons.collections.map.ReferenceMap;
/**
* ObjectStore stores objects using their ObjectId as a key. It works as a
dedicated
@@ -72,10 +74,20 @@
// must be followed in any new related developments.
public class ObjectStore implements Serializable, SnapshotEventListener,
GraphManager {
+ /**
+ * Factory method to create default Map for storing registered objects.
+ *
+ * @since 3.0
+ * @return a map with hard referenced keys and weak referenced values.
+ */
+ static Map createObjectMap() {
+ return new ReferenceMap(AbstractReferenceMap.HARD,
AbstractReferenceMap.WEAK);
+ }
+
protected transient Map newObjectsMap;
- protected Map objectMap = new HashMap();
- protected Map changes = new HashMap();
+ protected Map objectMap;
+ protected Map changes;
// a sequential id used to tag GraphDiffs so that they can later be sorted
in the
// original creation order
@@ -100,10 +112,24 @@
protected DataContext context;
public ObjectStore() {
+ this(null);
}
public ObjectStore(DataRowStore dataRowCache) {
+ this(dataRowCache, null);
+ }
+
+ /**
+ * Creates an ObjectStore with [EMAIL PROTECTED] DataRowStore} and a map
to use for storing
+ * registered objects. Passed map doesn't require any special
synchronization
+ * behavior, as ObjectStore is synchronized itself.
+ *
+ * @since 3.0
+ */
+ public ObjectStore(DataRowStore dataRowCache, Map objectMap) {
setDataRowCache(dataRowCache);
+ this.objectMap = objectMap != null ? objectMap :
ObjectStore.createObjectMap();
+ this.changes = new HashMap();
}
/**
Added:
incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/test/java/org/apache/cayenne/access/ObjectStoreGCTst.java
URL:
http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/test/java/org/apache/cayenne/access/ObjectStoreGCTst.java?view=auto&rev=478018
==============================================================================
---
incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/test/java/org/apache/cayenne/access/ObjectStoreGCTst.java
(added)
+++
incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/test/java/org/apache/cayenne/access/ObjectStoreGCTst.java
Tue Nov 21 19:22:16 2006
@@ -0,0 +1,115 @@
+/*****************************************************************
+ * 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
+ *
+ * http://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.art.Artist;
+import org.apache.cayenne.DataObjectUtils;
+import org.apache.cayenne.query.SQLTemplate;
+import org.apache.cayenne.query.SelectQuery;
+import org.apache.cayenne.unit.CayenneTestCase;
+import org.apache.cayenne.unit.util.ThreadedTestHelper;
+
+public class ObjectStoreGCTst extends CayenneTestCase {
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ deleteTestData();
+ }
+
+ public void testReleaseUnreferenced() throws Exception {
+ final DataContext context = createDataContext();
+ context.performGenericQuery(new SQLTemplate(
+ Artist.class,
+ "insert into ARTIST (ARTIST_ID, ARTIST_NAME) values (1,
'aa')"));
+
+ assertEquals(0, context.getObjectStore().registeredObjectsCount());
+ context.performQuery(new SelectQuery(Artist.class));
+ assertEquals(1, context.getObjectStore().registeredObjectsCount());
+
+ // allow for slow GC
+ new ThreadedTestHelper() {
+
+ protected void assertResult() throws Exception {
+ System.gc();
+ assertEquals(0,
context.getObjectStore().registeredObjectsCount());
+ }
+ }.assertWithTimeout(2000);
+ }
+
+ public void testRetainUnreferencedNew() throws Exception {
+ final DataContext context = createDataContext();
+
+ assertEquals(0, context.getObjectStore().registeredObjectsCount());
+ Artist a = (Artist) context.newObject(Artist.class);
+ a.setArtistName("X");
+ a = null;
+ assertEquals(1, context.getObjectStore().registeredObjectsCount());
+
+ // allow for slow GC
+ new ThreadedTestHelper() {
+
+ protected void assertResult() throws Exception {
+ System.gc();
+ assertEquals(1,
context.getObjectStore().registeredObjectsCount());
+ }
+ }.assertWithTimeout(2000);
+
+ assertEquals(1, context.getObjectStore().registeredObjectsCount());
+ context.commitChanges();
+ new ThreadedTestHelper() {
+
+ protected void assertResult() throws Exception {
+ System.gc();
+ assertEquals(0,
context.getObjectStore().registeredObjectsCount());
+ }
+ }.assertWithTimeout(2000);
+
+ }
+
+ public void testRetainUnreferencedModified() throws Exception {
+ final DataContext context = createDataContext();
+ context.performGenericQuery(new SQLTemplate(
+ Artist.class,
+ "insert into ARTIST (ARTIST_ID, ARTIST_NAME) values (1,
'aa')"));
+
+ assertEquals(0, context.getObjectStore().registeredObjectsCount());
+ Artist a = (Artist) DataObjectUtils.objectForPK(context, Artist.class,
1);
+ a.setArtistName("Y");
+ a = null;
+ assertEquals(1, context.getObjectStore().registeredObjectsCount());
+
+ new ThreadedTestHelper() {
+
+ protected void assertResult() throws Exception {
+ System.gc();
+ assertEquals(1,
context.getObjectStore().registeredObjectsCount());
+ }
+ }.assertWithTimeout(2000);
+
+ context.commitChanges();
+ new ThreadedTestHelper() {
+
+ protected void assertResult() throws Exception {
+ System.gc();
+ assertEquals(0,
context.getObjectStore().registeredObjectsCount());
+ }
+ }.assertWithTimeout(2000);
+
+ }
+}