You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2006/05/07 22:08:19 UTC

svn commit: r404834 - in /incubator/cayenne/main/trunk/cayenne/cayenne-java/src: cayenne/java/org/objectstyle/cayenne/ cayenne/java/org/objectstyle/cayenne/access/ tests/java/org/objectstyle/cayenne/access/ tests/resources/dml/

Author: aadamchik
Date: Sun May  7 13:08:17 2006
New Revision: 404834

URL: http://svn.apache.org/viewcvs?rev=404834&view=rev
Log:
CAY-544 - adding correct fault handling

Added:
    incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/access/DataContextFaults.java
Modified:
    incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/Fault.java
    incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/access/DataDomain.java
    incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/access/ObjectDiff.java
    incubator/cayenne/main/trunk/cayenne/cayenne-java/src/tests/java/org/objectstyle/cayenne/access/DataContextExtrasTst.java
    incubator/cayenne/main/trunk/cayenne/cayenne-java/src/tests/resources/dml/access.DataContextExtrasTst.xml

Modified: incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/Fault.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/Fault.java?rev=404834&r1=404833&r2=404834&view=diff
==============================================================================
--- incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/Fault.java (original)
+++ incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/Fault.java Sun May  7 13:08:17 2006
@@ -56,10 +56,6 @@
 package org.objectstyle.cayenne;
 
 import java.io.Serializable;
-import java.util.List;
-
-import org.objectstyle.cayenne.access.ToManyList;
-import org.objectstyle.cayenne.query.RelationshipQuery;
 
 /**
  * Represents a placeholder for an unresolved relationship from a source object. Fault is
@@ -70,13 +66,12 @@
  * @since 1.1
  * @author Andrei Adamchik
  */
-
 // TODO: serialization of faults should take into account the fact that
 // they are used as singletons to avoid duplicate creation on deserialization
 public abstract class Fault implements Serializable {
 
-    protected static final Fault toOneFault = new ToOneFault();
-    protected static final Fault toManyFault = new ToManyFault();
+    protected static Fault toOneFault;
+    protected static Fault toManyFault;
 
     public static Fault getToOneFault() {
         return toOneFault;
@@ -93,46 +88,4 @@
      * Returns an object for a given source object and relationship.
      */
     public abstract Object resolveFault(Persistent sourceObject, String relationshipName);
-
-    final static class ToManyFault extends Fault {
-
-        /**
-         * Resolves this fault to a List of objects.
-         */
-        public Object resolveFault(Persistent sourceObject, String relationshipName) {
-            return new ToManyList(sourceObject, relationshipName);
-        }
-    }
-
-    final static class ToOneFault extends Fault {
-
-        /**
-         * Resolves this fault to a DataObject.
-         */
-        public Object resolveFault(Persistent sourceObject, String relationshipName) {
-            ObjectContext context = sourceObject.getObjectContext();
-
-            RelationshipQuery query = new RelationshipQuery(
-                    sourceObject.getObjectId(),
-                    relationshipName,
-                    false);
-
-            List objects = context.performQuery(query);
-
-            if (objects.isEmpty()) {
-                return null;
-            }
-            else if (objects.size() == 1) {
-                return objects.get(0);
-            }
-            else {
-                throw new CayenneRuntimeException("Error resolving to-one fault. "
-                        + "More than one object found. "
-                        + "Source Id: "
-                        + sourceObject.getObjectId()
-                        + ", relationship: "
-                        + relationshipName);
-            }
-        }
-    }
 }

Added: incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/access/DataContextFaults.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/access/DataContextFaults.java?rev=404834&view=auto
==============================================================================
--- incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/access/DataContextFaults.java (added)
+++ incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/access/DataContextFaults.java Sun May  7 13:08:17 2006
@@ -0,0 +1,162 @@
+/* ====================================================================
+ * 
+ * The ObjectStyle Group Software License, version 1.1
+ * ObjectStyle Group - http://objectstyle.org/
+ * 
+ * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
+ * of the software. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 
+ * 3. The end-user documentation included with the redistribution, if any,
+ *    must include the following acknowlegement:
+ *    "This product includes software developed by independent contributors
+ *    and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ * 
+ * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
+ *    or promote products derived from this software without prior written
+ *    permission. For written permission, email
+ *    "andrus at objectstyle dot org".
+ * 
+ * 5. Products derived from this software may not be called "ObjectStyle"
+ *    or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
+ *    names without prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ * 
+ * This software consists of voluntary contributions made by many
+ * individuals and hosted on ObjectStyle Group web site.  For more
+ * information on the ObjectStyle Group, please see
+ * <http://objectstyle.org/>.
+ */
+package org.objectstyle.cayenne.access;
+
+import java.util.List;
+
+import org.objectstyle.cayenne.CayenneRuntimeException;
+import org.objectstyle.cayenne.Fault;
+import org.objectstyle.cayenne.ObjectContext;
+import org.objectstyle.cayenne.PersistenceState;
+import org.objectstyle.cayenne.Persistent;
+import org.objectstyle.cayenne.query.RelationshipQuery;
+
+/**
+ * A helper class that initializes server-side fault singletons.
+ * 
+ * @since 1.2
+ * @author Andrus Adamchik
+ */
+class DataContextFaults {
+
+    /**
+     * Resets super singletons.
+     */
+    static void init() {
+        ToManyFault.init();
+        ToOneFault.init();
+    }
+
+    final static class ToManyFault extends Fault {
+
+        /**
+         * Resets super singletons.
+         */
+        static void init() {
+            if (Fault.toManyFault == null) {
+                Fault.toManyFault = new ToManyFault();
+            }
+        }
+
+        /**
+         * Resolves this fault to a List of objects.
+         */
+        public Object resolveFault(Persistent sourceObject, String relationshipName) {
+            return new ToManyList(sourceObject, relationshipName);
+        }
+    }
+
+    final static class ToOneFault extends Fault {
+
+        /**
+         * Resets super singletons.
+         */
+        static void init() {
+            if (Fault.toOneFault == null) {
+                Fault.toOneFault = new ToOneFault();
+            }
+        }
+
+        /**
+         * Resolves this fault to a DataObject.
+         */
+        public Object resolveFault(Persistent sourceObject, String relationshipName) {
+            Object target = doResolveFault(sourceObject, relationshipName);
+
+            // must update the diff for the object
+            int state = sourceObject.getPersistenceState();
+            ObjectContext context = sourceObject.getObjectContext();
+            if ((state == PersistenceState.MODIFIED || state == PersistenceState.DELETED)
+                    && context instanceof DataContext) {
+
+                ObjectDiff diff = (ObjectDiff) ((DataContext) context)
+                        .getObjectStore()
+                        .getChangesByObjectId()
+                        .get(sourceObject.getObjectId());
+
+                if (diff != null) {
+                    diff.updateArcSnapshot(relationshipName, (Persistent) target);
+                }
+            }
+
+            return target;
+        }
+
+        Object doResolveFault(Persistent sourceObject, String relationshipName) {
+            RelationshipQuery query = new RelationshipQuery(
+                    sourceObject.getObjectId(),
+                    relationshipName,
+                    false);
+
+            List objects = sourceObject.getObjectContext().performQuery(query);
+
+            if (objects.isEmpty()) {
+                return null;
+            }
+            else if (objects.size() == 1) {
+                return objects.get(0);
+            }
+            else {
+                throw new CayenneRuntimeException("Error resolving to-one fault. "
+                        + "More than one object found. "
+                        + "Source Id: "
+                        + sourceObject.getObjectId()
+                        + ", relationship: "
+                        + relationshipName);
+            }
+        }
+    }
+}

Modified: incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/access/DataDomain.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/access/DataDomain.java?rev=404834&r1=404833&r2=404834&view=diff
==============================================================================
--- incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/access/DataDomain.java (original)
+++ incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/access/DataDomain.java Sun May  7 13:08:17 2006
@@ -147,6 +147,7 @@
      * Creates a DataDomain and assigns it a name.
      */
     public DataDomain(String name) {
+        DataContextFaults.init();
         setName(name);
         resetProperties();
     }

Modified: incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/access/ObjectDiff.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/access/ObjectDiff.java?rev=404834&r1=404833&r2=404834&view=diff
==============================================================================
--- incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/access/ObjectDiff.java (original)
+++ incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/access/ObjectDiff.java Sun May  7 13:08:17 2006
@@ -7,6 +7,7 @@
 import java.util.Map;
 
 import org.objectstyle.cayenne.CayenneRuntimeException;
+import org.objectstyle.cayenne.Fault;
 import org.objectstyle.cayenne.ObjectId;
 import org.objectstyle.cayenne.PersistenceState;
 import org.objectstyle.cayenne.Persistent;
@@ -50,6 +51,10 @@
                 || state == PersistenceState.DELETED
                 || state == PersistenceState.MODIFIED) {
 
+            ObjEntity entity = objectStore.getContext().getEntityResolver().getObjEntity(
+                    object.getObjectId().getEntityName());
+            final boolean lock = entity.getLockType() == ObjEntity.LOCK_TYPE_OPTIMISTIC;
+
             this.snapshot = new HashMap();
             this.arcSnapshot = new HashMap();
 
@@ -65,7 +70,17 @@
                 }
 
                 public boolean visitSingleObjectArc(SingleObjectArcProperty property) {
-                    takeSingleObjectArcSnapshot(property, object);
+
+                    // eagerly resolve optimistically locked relationships
+                    Object target = lock ? property.readProperty(object) : property
+                            .readPropertyDirectly(object);
+
+                    if (target instanceof Persistent) {
+                        target = ((Persistent) target).getObjectId();
+                    }
+                    // else - null || Fault
+
+                    arcSnapshot.put(property.getName(), target);
                     return true;
                 }
             });
@@ -77,7 +92,19 @@
     }
 
     ObjectId getArcSnapshotValue(String propertyName) {
-        return arcSnapshot != null ? (ObjectId) arcSnapshot.get(propertyName) : null;
+        Object value = arcSnapshot != null
+                ? arcSnapshot.get(propertyName)
+                : null;
+
+        if (value instanceof Fault) {
+            Persistent source = (Persistent) objectStore.getNode(nodeId);
+            Persistent target = (Persistent) ((Fault) value).resolveFault(source, propertyName);
+            
+            value = target != null ? target.getObjectId() : null;
+            arcSnapshot.put(propertyName, value);
+        }
+        
+        return (ObjectId) value;
     }
 
     boolean containsArcSnapshot(String propertyName) {
@@ -113,7 +140,59 @@
         otherDiffs.add(diff);
 
         if (diff instanceof ArcOperation) {
-            takeArcSnapshot((ArcOperation) diff);
+
+            ArcOperation arcDiff = (ArcOperation) diff;
+
+            if (snapshot == null) {
+                return;
+            }
+
+            Object targetId = arcDiff.getTargetNodeId();
+            String arcId = arcDiff.getArcId().toString();
+
+            Persistent object = (Persistent) objectStore.getNode(nodeId);
+            Property property = getClassDescriptor().getProperty(arcId);
+
+            // note that some collection properties implement 'SingleObjectArcProperty',
+            // so we cant't do 'instanceof SingleObjectArcProperty'
+            // TODO: andrus, 3.22.2006 - should we consider this a bug?
+
+            if (property instanceof CollectionProperty) {
+
+                // record flattened op changes
+                ObjEntity entity = objectStore.context.getEntityResolver().getObjEntity(
+                        object.getObjectId().getEntityName());
+
+                ObjRelationship relationship = (ObjRelationship) entity
+                        .getRelationship(property.getName());
+                if (relationship.isFlattened()) {
+
+                    if (flatIds == null) {
+                        flatIds = new HashMap();
+                    }
+
+                    ArcOperation oldOp = (ArcOperation) flatIds.put(arcDiff, arcDiff);
+
+                    // "delete" cancels "create" and vice versa...
+                    if (oldOp != null && oldOp.isDelete() != arcDiff.isDelete()) {
+                        flatIds.remove(arcDiff);
+                    }
+                }
+            }
+            else if (property instanceof SingleObjectArcProperty) {
+
+                if (currentArcSnapshot == null) {
+                    currentArcSnapshot = new HashMap();
+                }
+
+                currentArcSnapshot.put(arcId, targetId);
+            }
+            else {
+                String message = (property == null)
+                        ? "No property for arcId " + arcId
+                        : "Unrecognized property for arcId " + arcId + ": " + property;
+                throw new CayenneRuntimeException(message);
+            }
         }
     }
 
@@ -164,11 +243,15 @@
                     return true;
                 }
 
-                Object oldValue = arcSnapshot.get(property.getName());
-                Persistent newValue = (Persistent) property.readProperty(object);
+                Object newValue = property.readPropertyDirectly(object);
+                if (newValue instanceof Fault) {
+                    return true;
+                }
 
-                if (!Util.nullSafeEquals(oldValue, newValue != null ? newValue
-                        .getObjectId() : null)) {
+                Object oldValue = arcSnapshot.get(property.getName());
+                if (!Util.nullSafeEquals(oldValue, newValue != null
+                        ? ((Persistent) newValue).getObjectId()
+                        : null)) {
                     modFound[0] = true;
                 }
 
@@ -240,71 +323,15 @@
         });
     }
 
-    private void takeArcSnapshot(ArcOperation operation) {
-
-        if (snapshot == null) {
-            return;
-        }
-
-        Object targetId = operation.getTargetNodeId();
-        String arcId = operation.getArcId().toString();
-
-        Persistent object = (Persistent) objectStore.getNode(nodeId);
-        Property property = getClassDescriptor().getProperty(arcId);
-
-        // note that some collection properties implement 'SingleObjectArcProperty',
-        // so we cant't do 'instanceof SingleObjectArcProperty'
-        // TODO: andrus, 3.22.2006 - should we consider this a bug?
-
-        if (property instanceof CollectionProperty) {
-
-            // record flattened op changes
-            ObjEntity entity = objectStore.context.getEntityResolver().getObjEntity(
-                    object.getObjectId().getEntityName());
-
-            ObjRelationship relationship = (ObjRelationship) entity
-                    .getRelationship(property.getName());
-            if (relationship.isFlattened()) {
-
-                if (flatIds == null) {
-                    flatIds = new HashMap();
-                }
-
-                ArcOperation oldOp = (ArcOperation) flatIds.put(operation, operation);
-
-                // "delete" cancels "create" and vice versa...
-                if (oldOp != null && oldOp.isDelete() != operation.isDelete()) {
-                    flatIds.remove(operation);
-                }
-            }
-        }
-        else if (property instanceof SingleObjectArcProperty) {
-            takeSingleObjectArcSnapshot(property, object);
-
-            if (currentArcSnapshot == null) {
-                currentArcSnapshot = new HashMap();
-            }
-
-            currentArcSnapshot.put(arcId, targetId);
-        }
-        else {
-            String message = (property == null)
-                    ? "No property for arcId " + arcId
-                    : "Unrecognized property for arcId " + arcId + ": " + property;
-            throw new CayenneRuntimeException(message);
-        }
-    }
-
-    void takeSingleObjectArcSnapshot(Property property, Persistent object) {
+    /**
+     * This is used to update faults.
+     */
+    void updateArcSnapshot(String propertyName, Persistent object) {
         if (arcSnapshot == null) {
             arcSnapshot = new HashMap();
         }
 
-        if (!arcSnapshot.containsKey(property.getName())) {
-            Persistent oldTarget = (Persistent) property.readProperty(object);
-            arcSnapshot.put(property.getName(), oldTarget != null ? oldTarget
-                    .getObjectId() : null);
-        }
+        arcSnapshot.put(propertyName, object != null ? object.getObjectId() : null);
     }
 
     ClassDescriptor getClassDescriptor() {

Modified: incubator/cayenne/main/trunk/cayenne/cayenne-java/src/tests/java/org/objectstyle/cayenne/access/DataContextExtrasTst.java
URL: http://svn.apache.org/viewcvs/incubator/cayenne/main/trunk/cayenne/cayenne-java/src/tests/java/org/objectstyle/cayenne/access/DataContextExtrasTst.java?rev=404834&r1=404833&r2=404834&view=diff
==============================================================================
--- incubator/cayenne/main/trunk/cayenne/cayenne-java/src/tests/java/org/objectstyle/cayenne/access/DataContextExtrasTst.java (original)
+++ incubator/cayenne/main/trunk/cayenne/cayenne-java/src/tests/java/org/objectstyle/cayenne/access/DataContextExtrasTst.java Sun May  7 13:08:17 2006
@@ -322,9 +322,9 @@
         assertFalse(a1.isValidateForSaveCalled());
     }
 
-    public void testPhantomModificationsValidate2() throws Exception {
+    public void testPhantomModificationsValidateToOne() throws Exception {
         deleteTestData();
-        createTestData("testPhantomModification2");
+        createTestData("testPhantomModificationsValidateToOne");
         DataContext context = createDataContext();
 
         List objects = context.performQuery(new SelectQuery(Painting.class));
@@ -334,7 +334,25 @@
         p1.resetValidationFlags();
         context.commitChanges();
 
-        assertFalse(p1.isValidateForSaveCalled());
+        assertFalse("To-one relationship presence caused incorrect validation call.", p1
+                .isValidateForSaveCalled());
+    }
+
+    public void testValidateOnToManyChange() throws Exception {
+        deleteTestData();
+        createTestData("testValidateOnToManyChange");
+        DataContext context = createDataContext();
+
+        List objects = context.performQuery(new SelectQuery(Artist.class));
+        Artist a1 = (Artist) objects.get(0);
+
+        Painting p1 = (Painting) context.newObject(Painting.class);
+        p1.setPaintingTitle("XXX");
+        a1.addToPaintingArray(p1);
+        a1.resetValidationFlags();
+        context.commitChanges();
+
+        assertFalse(a1.isValidateForSaveCalled());
     }
 
     public void testPhantomAttributeModificationCommit() throws Exception {

Modified: incubator/cayenne/main/trunk/cayenne/cayenne-java/src/tests/resources/dml/access.DataContextExtrasTst.xml
URL: http://svn.apache.org/viewcvs/incubator/cayenne/main/trunk/cayenne/cayenne-java/src/tests/resources/dml/access.DataContextExtrasTst.xml?rev=404834&r1=404833&r2=404834&view=diff
==============================================================================
--- incubator/cayenne/main/trunk/cayenne/cayenne-java/src/tests/resources/dml/access.DataContextExtrasTst.xml (original)
+++ incubator/cayenne/main/trunk/cayenne/cayenne-java/src/tests/resources/dml/access.DataContextExtrasTst.xml Sun May  7 13:08:17 2006
@@ -38,11 +38,18 @@
 			</list>
 		</constructor-arg>
 	</bean>
-		<bean id="testPhantomModification2" class="java.util.ArrayList">
+	<bean id="testPhantomModificationsValidateToOne" class="java.util.ArrayList">
 		<constructor-arg>
 			<list>
 				<ref bean="A1"/>
 				<ref bean="P11"/>
+			</list>
+		</constructor-arg>
+	</bean>
+		<bean id="testValidateOnToManyChange" class="java.util.ArrayList">
+		<constructor-arg>
+			<list>
+				<ref bean="A1"/>
 			</list>
 		</constructor-arg>
 	</bean>