Return-Path: Delivered-To: apmail-cayenne-commits-archive@www.apache.org Received: (qmail 13620 invoked from network); 1 Mar 2008 15:22:00 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 1 Mar 2008 15:22:00 -0000 Received: (qmail 27454 invoked by uid 500); 1 Mar 2008 15:21:56 -0000 Delivered-To: apmail-cayenne-commits-archive@cayenne.apache.org Received: (qmail 27434 invoked by uid 500); 1 Mar 2008 15:21:56 -0000 Mailing-List: contact commits-help@cayenne.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cayenne.apache.org Delivered-To: mailing list commits@cayenne.apache.org Received: (qmail 27425 invoked by uid 99); 1 Mar 2008 15:21:56 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 01 Mar 2008 07:21:56 -0800 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO eris.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 01 Mar 2008 15:21:16 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id E5DF81A9832; Sat, 1 Mar 2008 07:21:34 -0800 (PST) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r632599 - in /cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src: main/java/org/apache/cayenne/access/ main/java/org/apache/cayenne/reflect/pojo/ main/java/org/apache/cayenne/reflect/valueholder/ test/java/org/apache/cayenne/remote/ Date: Sat, 01 Mar 2008 15:21:33 -0000 To: commits@cayenne.apache.org From: aadamchik@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20080301152134.E5DF81A9832@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: aadamchik Date: Sat Mar 1 07:21:32 2008 New Revision: 632599 URL: http://svn.apache.org/viewvc?rev=632599&view=rev Log: CAY-789 return object diff to client in ROP after commit (first working implementation. more testing is needed; need to filter out server entities) Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ChildDiffLoader.java cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataContext.java cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ObjectStore.java cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/pojo/EnhancedPojoMapProperty.java cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/valueholder/ValueHolderMapProperty.java cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/remote/ClientChannelServerDiffsTest.java Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ChildDiffLoader.java URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ChildDiffLoader.java?rev=632599&r1=632598&r2=632599&view=diff ============================================================================== --- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ChildDiffLoader.java (original) +++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ChildDiffLoader.java Sat Mar 1 07:21:32 2008 @@ -50,7 +50,33 @@ */ class ChildDiffLoader implements GraphChangeHandler { - ObjectContext context; + static final ThreadLocal childDiffProcessing = new ThreadLocal() { + + @Override + protected synchronized Boolean initialValue() { + return Boolean.FALSE; + } + }; + + private ObjectContext context; + + /** + * Returns whether child diff processing is in progress. + * + * @since 3.0 + */ + static boolean isProcessingChildDiff() { + return childDiffProcessing.get(); + } + + /** + * Sets whether child diff processing is in progress. + * + * @since 3.0 + */ + static void setExternalChange(Boolean flag) { + childDiffProcessing.set(flag); + } ChildDiffLoader(ObjectContext context) { this.context = context; @@ -61,30 +87,47 @@ } public void nodeCreated(Object nodeId) { - ObjectId id = (ObjectId) nodeId; - if (id.getEntityName() == null) { - throw new NullPointerException("Null entity name in id " + id); - } - ObjEntity entity = context.getEntityResolver().getObjEntity(id.getEntityName()); - if (entity == null) { - throw new IllegalArgumentException("Entity not mapped with Cayenne: " + id); - } + setExternalChange(Boolean.TRUE); - Persistent dataObject = null; try { - dataObject = (Persistent) entity.getJavaClass().newInstance(); + ObjectId id = (ObjectId) nodeId; + if (id.getEntityName() == null) { + throw new NullPointerException("Null entity name in id " + id); + } + + ObjEntity entity = context.getEntityResolver().getObjEntity( + id.getEntityName()); + if (entity == null) { + throw new IllegalArgumentException("Entity not mapped with Cayenne: " + + id); + } + + Persistent dataObject = null; + try { + dataObject = (Persistent) entity.getJavaClass().newInstance(); + } + catch (Exception ex) { + throw new CayenneRuntimeException("Error instantiating object.", ex); + } + + dataObject.setObjectId(id); + context.registerNewObject(dataObject); } - catch (Exception ex) { - throw new CayenneRuntimeException("Error instantiating object.", ex); + finally { + setExternalChange(Boolean.FALSE); } - - dataObject.setObjectId(id); - context.registerNewObject(dataObject); } public void nodeRemoved(Object nodeId) { - context.deleteObject(findObject(nodeId)); + setExternalChange(Boolean.TRUE); + + try { + context.deleteObject(findObject(nodeId)); + } + finally { + setExternalChange(Boolean.FALSE); + } } public void nodePropertyChanged( @@ -99,12 +142,16 @@ ClassDescriptor descriptor = context.getEntityResolver().getClassDescriptor( ((ObjectId) nodeId).getEntityName()); + setExternalChange(Boolean.TRUE); try { descriptor.getProperty(property).writeProperty(object, null, newValue); } catch (Exception e) { throw new CayenneRuntimeException("Error setting property: " + property, e); } + finally { + setExternalChange(Boolean.FALSE); + } } public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) { @@ -116,27 +163,33 @@ ((ObjectId) nodeId).getEntityName()); ArcProperty property = (ArcProperty) descriptor.getProperty(arcId.toString()); - property.visit(new PropertyVisitor() { + setExternalChange(Boolean.TRUE); + try { + property.visit(new PropertyVisitor() { - public boolean visitAttribute(AttributeProperty property) { - return false; - } + public boolean visitAttribute(AttributeProperty property) { + return false; + } - public boolean visitToMany(ToManyProperty property) { - // connect reverse arc if the relationship is marked as "runtime" - ArcProperty reverseArc = property.getComplimentaryReverseArc(); - boolean autoConnectReverse = reverseArc != null - && reverseArc.getRelationship().isRuntime(); + public boolean visitToMany(ToManyProperty property) { + // connect reverse arc if the relationship is marked as "runtime" + ArcProperty reverseArc = property.getComplimentaryReverseArc(); + boolean autoConnectReverse = reverseArc != null + && reverseArc.getRelationship().isRuntime(); - property.addTarget(source, target, autoConnectReverse); - return false; - } + property.addTarget(source, target, autoConnectReverse); + return false; + } - public boolean visitToOne(ToOneProperty property) { - property.setTarget(source, target, false); - return false; - } - }); + public boolean visitToOne(ToOneProperty property) { + property.setTarget(source, target, false); + return false; + } + }); + } + finally { + setExternalChange(Boolean.FALSE); + } } public void arcDeleted(Object nodeId, final Object targetNodeId, Object arcId) { @@ -151,45 +204,54 @@ ClassDescriptor descriptor = context.getEntityResolver().getClassDescriptor( ((ObjectId) nodeId).getEntityName()); Property property = descriptor.getProperty(arcId.toString()); - property.visit(new PropertyVisitor() { - public boolean visitAttribute(AttributeProperty property) { - return false; - } - - public boolean visitToMany(ToManyProperty property) { - // connect reverse arc if the relationship is marked as "runtime" - ArcProperty reverseArc = property.getComplimentaryReverseArc(); - boolean autoConnectReverse = reverseArc != null - && reverseArc.getRelationship().isRuntime(); + setExternalChange(Boolean.TRUE); + try { + property.visit(new PropertyVisitor() { - Persistent target = findObject(targetNodeId); + public boolean visitAttribute(AttributeProperty property) { + return false; + } - if (target == null) { + public boolean visitToMany(ToManyProperty property) { + // connect reverse arc if the relationship is marked as "runtime" + ArcProperty reverseArc = property.getComplimentaryReverseArc(); + boolean autoConnectReverse = reverseArc != null + && reverseArc.getRelationship().isRuntime(); + + Persistent target = findObject(targetNodeId); + + if (target == null) { + + // this is usually the case when a NEW object was deleted and then + // its + // relationships were manipulated; so try to locate the object in + // the + // collection ... + // the performance of this is rather dubious of course... + target = findObjectInCollection(targetNodeId, property + .readProperty(source)); + } + + if (target == null) { + // ignore? + } + else { + property.removeTarget(source, target, autoConnectReverse); + } - // this is usually the case when a NEW object was deleted and then its - // relationships were manipulated; so try to locate the object in the - // collection ... - // the performance of this is rather dubious of course... - target = findObjectInCollection(targetNodeId, property - .readProperty(source)); + return false; } - if (target == null) { - // ignore? - } - else { - property.removeTarget(source, target, autoConnectReverse); + public boolean visitToOne(ToOneProperty property) { + property.setTarget(source, null, false); + return false; } - - return false; - } - - public boolean visitToOne(ToOneProperty property) { - property.setTarget(source, null, false); - return false; - } - }); + }); + } + finally { + setExternalChange(Boolean.FALSE); + } } Persistent findObject(Object nodeId) { @@ -200,9 +262,9 @@ if (object != null) { return object; } - + ObjectId id = (ObjectId) nodeId; - + // this can happen if a NEW object is deleted and after that its relationships are // modified if (id.isTemporary()) { @@ -225,18 +287,19 @@ return (Persistent) objects.get(0); } - + Persistent findObjectInCollection(Object nodeId, Object toManyHolder) { - Collection c = (toManyHolder instanceof Map) ? ((Map) toManyHolder) - .values() : (Collection) toManyHolder; + Collection c = (toManyHolder instanceof Map) + ? ((Map) toManyHolder).values() + : (Collection) toManyHolder; Iterator it = c.iterator(); while (it.hasNext()) { Persistent o = (Persistent) it.next(); - if(nodeId.equals(o.getObjectId())) { + if (nodeId.equals(o.getObjectId())) { return o; } } - + return null; } } Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataContext.java URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataContext.java?rev=632599&r1=632598&r2=632599&view=diff ============================================================================== --- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataContext.java (original) +++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/DataContext.java Sat Mar 1 07:21:32 2008 @@ -1116,12 +1116,22 @@ GraphDiff changes, boolean cascade) { - if (this != originatingContext && changes != null) { - changes.apply(new ChildDiffLoader(this)); - fireDataChannelChanged(originatingContext, changes); - } + boolean childContext = this != originatingContext && changes != null; + + try { + if (childContext) { + getObjectStore().childContextSyncStarted(); + changes.apply(new ChildDiffLoader(this)); + fireDataChannelChanged(originatingContext, changes); + } - return (cascade) ? flushToParent(true) : new CompoundDiff(); + return (cascade) ? flushToParent(true) : new CompoundDiff(); + } + finally { + if (childContext) { + getObjectStore().childContextSyncStopped(); + } + } } /** @@ -1141,6 +1151,7 @@ : DataChannel.FLUSH_NOCASCADE_SYNC; ObjectStore objectStore = getObjectStore(); + GraphDiff parentChanges = null; // prevent multiple commits occuring simulteneously synchronized (objectStore) { @@ -1153,46 +1164,62 @@ if (noop) { // need to clear phantom changes objectStore.postprocessAfterPhantomCommit(); - return new CompoundDiff(); } + else { - try { - GraphDiff returnChanges = getChannel().onSync(this, changes, syncType); + try { + parentChanges = getChannel().onSync(this, changes, syncType); - // note that this is a hack resulting from a fix to CAY-766... To support - // valid object state in PostPersist callback, 'postprocessAfterCommit' is - // invoked by DataDomain.onSync(..). Unless the parent is DataContext, and - // this method is not invoked!! As a result, PostPersist will contain temp - // ObjectIds in nested contexts and perm ones in flat contexts. - // Pending better callback design ..... - if (objectStore.hasChanges()) { - objectStore.postprocessAfterCommit(returnChanges); + // note that this is a hack resulting from a fix to CAY-766... To + // support + // valid object state in PostPersist callback, + // 'postprocessAfterCommit' is + // invoked by DataDomain.onSync(..). Unless the parent is DataContext, + // and + // this method is not invoked!! As a result, PostPersist will contain + // temp + // ObjectIds in nested contexts and perm ones in flat contexts. + // Pending better callback design ..... + if (objectStore.hasChanges()) { + objectStore.postprocessAfterCommit(parentChanges); + } + + // this event is caught by peer nested DataContexts to synchronize the + // state + fireDataChannelCommitted(this, changes); } - - // this event is caught by peer nested DataContexts to synchronize the - // state - fireDataChannelCommitted(this, changes); - - // this event is caught by child DataContexts to update temporary - // ObjectIds with permanent - if (!returnChanges.isNoop()) { - fireDataChannelCommitted(getChannel(), returnChanges); + // "catch" is needed to unwrap OptimisticLockExceptions + catch (CayenneRuntimeException ex) { + Throwable unwound = Util.unwindException(ex); + + if (unwound instanceof CayenneRuntimeException) { + throw (CayenneRuntimeException) unwound; + } + else { + throw new CayenneRuntimeException("Commit Exception", unwound); + } } + } + + // merge changes from parent as well as changes caused by lifecycle event + // callbacks/listeners... - return returnChanges; + CompoundDiff diff = new CompoundDiff(); + + diff.addAll(objectStore.getLifecycleEventInducedChanges()); + if (parentChanges != null) { + diff.add(parentChanges); } - // "catch" is needed to unwrap OptimisticLockExceptions - catch (CayenneRuntimeException ex) { - Throwable unwound = Util.unwindException(ex); - if (unwound instanceof CayenneRuntimeException) { - throw (CayenneRuntimeException) unwound; - } - else { - throw new CayenneRuntimeException("Commit Exception", unwound); - } + // this event is caught by child DataContexts to update temporary + // ObjectIds with permanent + if (!diff.isNoop()) { + fireDataChannelCommitted(getChannel(), diff); } + + return diff; } + } /** Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ObjectStore.java URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ObjectStore.java?rev=632599&r1=632598&r2=632599&view=diff ============================================================================== --- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ObjectStore.java (original) +++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/access/ObjectStore.java Sat Mar 1 07:21:32 2008 @@ -43,6 +43,7 @@ import org.apache.cayenne.graph.NodeCreateOperation; import org.apache.cayenne.graph.NodeDeleteOperation; import org.apache.cayenne.graph.NodeDiff; +import org.apache.cayenne.graph.NodePropertyChangeOperation; import org.apache.cayenne.map.DataMap; import org.apache.cayenne.map.DbEntity; import org.apache.cayenne.map.ObjEntity; @@ -105,6 +106,8 @@ // used to avoid incorrect on-demand DataRowStore initialization after deserialization private boolean dataRowCacheSet; + private Collection lifecycleEventInducedChanges; + /** * The DataContext that owns this ObjectStore. */ @@ -132,6 +135,39 @@ } /** + * @since 3.0 + */ + void childContextSyncStarted() { + lifecycleEventInducedChanges = new ArrayList(); + } + + /** + * @since 3.0 + */ + void childContextSyncStopped() { + lifecycleEventInducedChanges = null; + } + + /** + * @since 3.0 + */ + Collection getLifecycleEventInducedChanges() { + return lifecycleEventInducedChanges != null + ? lifecycleEventInducedChanges + : Collections.EMPTY_LIST; + } + + void registerLifecycleEventInducedChange(GraphDiff diff) { + if (ChildDiffLoader.isProcessingChildDiff()) { + // reset so that subsequent event-induced changes could get registered... + ChildDiffLoader.setExternalChange(Boolean.FALSE); + } + else { + lifecycleEventInducedChanges.add(diff); + } + } + + /** * Registers object change. * * @since 1.2 @@ -570,7 +606,7 @@ if (context != null && context.getChannel() != null) { ObjectIdQuery query = new ObjectIdQuery(oid, true, ObjectIdQuery.CACHE); - List results = context.getChannel().onQuery(context, query).firstList(); + List results = context.getChannel().onQuery(context, query).firstList(); return results.isEmpty() ? null : (DataRow) results.get(0); } else { @@ -998,14 +1034,27 @@ * @since 1.2 */ public void nodeCreated(Object nodeId) { - registerDiff(nodeId, new NodeCreateOperation(nodeId)); + NodeDiff diff = new NodeCreateOperation(nodeId); + + if (lifecycleEventInducedChanges != null) { + registerLifecycleEventInducedChange(diff); + } + + registerDiff(nodeId, diff); } /** * @since 1.2 */ public void nodeRemoved(Object nodeId) { - registerDiff(nodeId, new NodeDeleteOperation(nodeId)); + + NodeDiff diff = new NodeDeleteOperation(nodeId); + + if (lifecycleEventInducedChanges != null) { + registerLifecycleEventInducedChange(diff); + } + + registerDiff(nodeId, diff); } /** @@ -1019,6 +1068,14 @@ Object oldValue, Object newValue) { + if (lifecycleEventInducedChanges != null) { + registerLifecycleEventInducedChange(new NodePropertyChangeOperation( + nodeId, + property, + oldValue, + newValue)); + } + registerDiff(nodeId, null); } @@ -1026,22 +1083,26 @@ * @since 1.2 */ public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) { - registerDiff(nodeId, new ArcOperation( - nodeId, - targetNodeId, - arcId.toString(), - false)); + NodeDiff diff = new ArcOperation(nodeId, targetNodeId, arcId.toString(), false); + + if (lifecycleEventInducedChanges != null) { + registerLifecycleEventInducedChange(diff); + } + + registerDiff(nodeId, diff); } /** * @since 1.2 */ public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) { - registerDiff(nodeId, new ArcOperation( - nodeId, - targetNodeId, - arcId.toString(), - true)); + NodeDiff diff = new ArcOperation(nodeId, targetNodeId, arcId.toString(), true); + + if (lifecycleEventInducedChanges != null) { + registerLifecycleEventInducedChange(diff); + } + + registerDiff(nodeId, diff); } // an ObjectIdQuery optimized for retrieval of multiple snapshots - it can be reset Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/pojo/EnhancedPojoMapProperty.java URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/pojo/EnhancedPojoMapProperty.java?rev=632599&r1=632598&r2=632599&view=diff ============================================================================== --- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/pojo/EnhancedPojoMapProperty.java (original) +++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/pojo/EnhancedPojoMapProperty.java Sat Mar 1 07:21:32 2008 @@ -18,6 +18,8 @@ ****************************************************************/ package org.apache.cayenne.reflect.pojo; +import java.util.Map; + import org.apache.cayenne.Persistent; import org.apache.cayenne.ValueHolder; import org.apache.cayenne.reflect.Accessor; @@ -44,6 +46,36 @@ @Override protected ValueHolder createValueHolder(Persistent relationshipOwner) { return new PersistentObjectMap(relationshipOwner, getName(), mapKeyAccessor); + } + + @Override + public void addTarget(Object source, Object target, boolean setReverse) { + + if (target == null) { + throw new NullPointerException("Attempt to add null object."); + } + + // Now do the rest of the normal handling (regardless of whether it was + // flattened or not) + Map collection = (Map) readProperty(source); + collection.put(getMapKey(target), target); + + if (setReverse) { + setReverse(source, null, target); + } + } + + @Override + public void removeTarget(Object source, Object target, boolean setReverse) { + + // Now do the rest of the normal handling (regardless of whether it was + // flattened or not) + Map collection = (Map) readProperty(source); + collection.remove(getMapKey(target)); + + if (target != null && setReverse) { + setReverse(source, target, null); + } } public Object getMapKey(Object target) throws PropertyException { Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/valueholder/ValueHolderMapProperty.java URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/valueholder/ValueHolderMapProperty.java?rev=632599&r1=632598&r2=632599&view=diff ============================================================================== --- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/valueholder/ValueHolderMapProperty.java (original) +++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/valueholder/ValueHolderMapProperty.java Sat Mar 1 07:21:32 2008 @@ -18,6 +18,8 @@ ****************************************************************/ package org.apache.cayenne.reflect.valueholder; +import java.util.Map; + import org.apache.cayenne.Persistent; import org.apache.cayenne.ValueHolder; import org.apache.cayenne.reflect.Accessor; @@ -39,6 +41,36 @@ Accessor accessor, String reverseName, Accessor mapKeyAccessor) { super(owner, targetDescriptor, accessor, reverseName); this.mapKeyAccessor = mapKeyAccessor; + } + + @Override + public void addTarget(Object source, Object target, boolean setReverse) { + + if (target == null) { + throw new NullPointerException("Attempt to add null object."); + } + + // Now do the rest of the normal handling (regardless of whether it was + // flattened or not) + Map collection = (Map) readProperty(source); + collection.put(getMapKey(target), target); + + if (setReverse) { + setReverse(source, null, target); + } + } + + @Override + public void removeTarget(Object source, Object target, boolean setReverse) { + + // Now do the rest of the normal handling (regardless of whether it was + // flattened or not) + Map collection = (Map) readProperty(source); + collection.remove(getMapKey(target)); + + if (target != null && setReverse) { + setReverse(source, target, null); + } } @Override Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/remote/ClientChannelServerDiffsTest.java URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/remote/ClientChannelServerDiffsTest.java?rev=632599&r1=632598&r2=632599&view=diff ============================================================================== --- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/remote/ClientChannelServerDiffsTest.java (original) +++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/remote/ClientChannelServerDiffsTest.java Sat Mar 1 07:21:32 2008 @@ -31,6 +31,7 @@ import org.apache.cayenne.reflect.LifecycleCallbackRegistry; import org.apache.cayenne.remote.service.LocalConnection; import org.apache.cayenne.testdo.mt.ClientMtTable1; +import org.apache.cayenne.testdo.mt.ClientMtTable2; import org.apache.cayenne.testdo.mt.MtTable1; import org.apache.cayenne.unit.AccessStack; import org.apache.cayenne.unit.CayenneCase; @@ -88,7 +89,7 @@ public void testReturnDiffInPrePersist() { final List diffs = new ArrayList(); - final GraphChangeHandler diffReader = new NoopGraphChangeHandler() { + final NoopGraphChangeHandler diffReader = new NoopGraphChangeHandler() { @Override public void nodePropertyChanged( @@ -96,6 +97,8 @@ String property, Object oldValue, Object newValue) { + + super.nodePropertyChanged(nodeId, property, oldValue, newValue); diffs .add(new GenericDiff( (ObjectId) nodeId, @@ -103,6 +106,7 @@ oldValue, newValue)); } + }; LifecycleCallbackRegistry registry = getDomain() @@ -139,42 +143,95 @@ CayenneContext context = new CayenneContext(channel); ClientMtTable1 o = context.newObject(ClientMtTable1.class); + ObjectId tempId = o.getObjectId(); o.setServerAttribute1("YY"); context.commitChanges(); -// assertEquals(1, diffs.size()); -// assertEquals(o.getObjectId(), diffs.get(0).sourceId); -// assertEquals(ClientMtTable1.GLOBAL_ATTRIBUTE1_PROPERTY, diffs.get(0).property); -// assertNull(diffs.get(0).oldValue); -// assertEquals("XXX", diffs.get(0).newValue); + assertEquals(2, diffReader.size); + assertEquals(1, diffs.size()); + assertEquals(tempId, diffs.get(0).sourceId); + assertEquals(ClientMtTable1.GLOBAL_ATTRIBUTE1_PROPERTY, diffs.get(0).property); + assertNull(diffs.get(0).oldValue); + assertEquals("XXX", diffs.get(0).newValue); } finally { registry.clear(); } } + public void testReturnDiffClientArcChanges() { + + final NoopGraphChangeHandler diffReader = new NoopGraphChangeHandler(); + + ClientServerChannel csChannel = new ClientServerChannel(getDomain()); + ClientChannel channel = new ClientChannel(new LocalConnection(csChannel)) { + + @Override + public GraphDiff onSync( + ObjectContext originatingContext, + GraphDiff changes, + int syncType) { + + GraphDiff serverDiff = super + .onSync(originatingContext, changes, syncType); + + assertNotNull(serverDiff); + serverDiff.apply(diffReader); + return serverDiff; + } + }; + + CayenneContext context = new CayenneContext(channel); + ClientMtTable1 o = context.newObject(ClientMtTable1.class); + ClientMtTable2 o2 = context.newObject(ClientMtTable2.class); + o.addToTable2Array(o2); + context.commitChanges(); + + assertEquals(2, diffReader.size); + + diffReader.reset(); + + ClientMtTable2 o3 = context.newObject(ClientMtTable2.class); + o3.setTable1(o); + context.commitChanges(); + assertEquals(1, diffReader.size); + } + class NoopGraphChangeHandler implements GraphChangeHandler { + int size; + + void reset() { + size = 0; + } + + public void nodePropertyChanged( + Object nodeId, + String property, + Object oldValue, + Object newValue) { + + size++; + } + public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) { + size++; } public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) { + size++; } public void nodeCreated(Object nodeId) { + size++; } public void nodeIdChanged(Object nodeId, Object newId) { - } - - public void nodePropertyChanged( - Object nodeId, - String property, - Object oldValue, - Object newValue) { + size++; } public void nodeRemoved(Object nodeId) { + size++; } }