Fix infinite recursion in JsonRequestProcessor


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8633 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java b/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java
index a0974cf..1f0d454 100644
--- a/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java
+++ b/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java
@@ -149,6 +149,7 @@
    * 
    * <li>Find the changes that need to be sent back.
    */
+  private Map<EntityKey, Object> cachedEntityLookup = new HashMap<EntityKey, Object>();
   private Set<EntityKey> involvedKeys = new HashSet<EntityKey>();
   private Map<EntityKey, DvsData> dvsDataMap = new HashMap<EntityKey, DvsData>();
   private Map<EntityKey, SerializedEntity> beforeDataMap = new HashMap<EntityKey, SerializedEntity>();
@@ -287,6 +288,7 @@
       if (service != null) {
         Class<?> sClass = service.value();
         EntityKey entityKey = getEntityKey(parameterValue.toString());
+
         DvsData dvsData = dvsDataMap.get(entityKey);
         try {
           if (dvsData != null) {
@@ -294,7 +296,9 @@
                 dvsData.jsonObject, dvsData.writeOperation);
             return entityData.entityInstance;
           } else {
-            Method findMeth = sClass.getMethod(getMethodNameFromPropertyName(sClass.getSimpleName(), "find"), Long.class);
+            Method findMeth = sClass.getMethod(
+                getMethodNameFromPropertyName(sClass.getSimpleName(), "find"),
+                Long.class);
             return findMeth.invoke(null, entityKey.id);
           }
         } catch (NoSuchMethodException e) {
@@ -396,11 +400,14 @@
       Object entityInstance = getEntityInstance(writeOperation, entity,
           recordObject.get("id"), propertiesInRecord.get("id"));
 
+      cachedEntityLookup.put(entityKey, entityInstance);
+
       Set<ConstraintViolation<Object>> violations = Collections.emptySet();
       Iterator<?> keys = recordObject.keys();
       while (keys.hasNext()) {
         String key = (String) keys.next();
         Class<?> propertyType = propertiesInRecord.get(key);
+        Class<?> dtoType = propertiesToDTO.get(key);
         if (writeOperation == WriteOperation.CREATE && ("id".equals(key))) {
           Long id = generateIdForCreate(key);
           if (id != null) {
@@ -408,8 +415,20 @@
                 propertyType).invoke(entityInstance, id);
           }
         } else {
-          Object propertyValue = getPropertyValueFromRequest(recordObject, key,
-              propertiesToDTO.get(key));
+          Object propertyValue = null;
+          if (Record.class.isAssignableFrom(dtoType)) {
+            EntityKey propKey = getEntityKey(recordObject.getString(key));
+            Object cacheValue = cachedEntityLookup.get(propKey);
+            if (cachedEntityLookup.containsKey(propKey)) {
+              propertyValue = cacheValue;
+            } else {
+              propertyValue = getPropertyValueFromRequest(recordObject, key,
+               propertiesToDTO.get(key));
+            }
+          } else {
+             propertyValue = getPropertyValueFromRequest(recordObject, key,
+               propertiesToDTO.get(key));
+          }
           entity.getMethod(getMethodNameFromPropertyName(key, "set"),
               propertyType).invoke(entityInstance, propertyValue);
         }
diff --git a/user/test/com/google/gwt/requestfactory/client/impl/SimpleFooRecordImpl.java b/user/test/com/google/gwt/requestfactory/client/impl/SimpleFooRecordImpl.java
index 301bb8d..63a8e32 100644
--- a/user/test/com/google/gwt/requestfactory/client/impl/SimpleFooRecordImpl.java
+++ b/user/test/com/google/gwt/requestfactory/client/impl/SimpleFooRecordImpl.java
@@ -103,6 +103,10 @@
     return get(enumField);
   }
 
+  public SimpleFooRecord getFooField() {
+    return (SimpleFooRecord) getValueStore().getRecordBySchemaAndId(SCHEMA, (Long) (Object) get(fooField));
+  }
+
   public java.lang.Integer getIntId() {
     return get(intId);
   }
@@ -131,6 +135,10 @@
     // ignore
   }
 
+  public void setFooField(SimpleFooRecord fooField) {
+    // ignore
+  }
+
   public void setIntId(Integer intId) {
     // ignore
   }
diff --git a/user/test/com/google/gwt/valuestore/client/RequestFactoryTest.java b/user/test/com/google/gwt/valuestore/client/RequestFactoryTest.java
index 3ac6136..02733ec 100644
--- a/user/test/com/google/gwt/valuestore/client/RequestFactoryTest.java
+++ b/user/test/com/google/gwt/valuestore/client/RequestFactoryTest.java
@@ -204,6 +204,26 @@
     });
   }
 
+   public void testPersistRecursiveRelation() {
+    final SimpleRequestFactory req = GWT.create(SimpleRequestFactory.class);
+    HandlerManager hm = new HandlerManager(null);
+    req.init(hm);
+    delayTestFinish(5000);
+
+    SimpleFooRecord rayFoo = req.create(SimpleFooRecord.class);
+    final RequestObject<SimpleFooRecord> persistRay = req.simpleFooRequest().persistAndReturnSelf(
+        rayFoo);
+    rayFoo = persistRay.edit(rayFoo);
+    rayFoo.setUserName("Ray");
+    rayFoo.setFooField(rayFoo);
+    persistRay.fire(new Receiver<SimpleFooRecord>() {
+      public void onSuccess(final SimpleFooRecord persistedRay,
+          Set<SyncResult> ignored) {
+        finishTest();
+      }
+    });
+  }
+
   public void testPersistRelation() {
     final SimpleRequestFactory req = GWT.create(SimpleRequestFactory.class);
     HandlerManager hm = new HandlerManager(null);
diff --git a/user/test/com/google/gwt/valuestore/server/SimpleFoo.java b/user/test/com/google/gwt/valuestore/server/SimpleFoo.java
index 27a967f..aa7be4c 100644
--- a/user/test/com/google/gwt/valuestore/server/SimpleFoo.java
+++ b/user/test/com/google/gwt/valuestore/server/SimpleFoo.java
@@ -88,6 +88,8 @@
 
   private SimpleBar barField;
 
+  private SimpleFoo fooField;
+
   public SimpleFoo() {
     intId = 42;
     version = 1;
@@ -115,6 +117,10 @@
     return enumField;
   }
 
+  public SimpleFoo getFooField() {
+    return fooField;
+  }
+
   public Long getId() {
     return id;
   }
@@ -164,6 +170,10 @@
     this.enumField = enumField;
   }
 
+  public void setFooField(SimpleFoo fooField) {
+    this.fooField = fooField;
+  }
+
   public void setId(Long id) {
     this.id = id;
   }
diff --git a/user/test/com/google/gwt/valuestore/shared/SimpleFooRecord.java b/user/test/com/google/gwt/valuestore/shared/SimpleFooRecord.java
index b0cf7a2..a161c2b 100644
--- a/user/test/com/google/gwt/valuestore/shared/SimpleFooRecord.java
+++ b/user/test/com/google/gwt/valuestore/shared/SimpleFooRecord.java
@@ -42,6 +42,9 @@
   Property<SimpleBarRecord> barField = new Property<SimpleBarRecord>("barField",
       SimpleBarRecord.class);
 
+  Property<SimpleFooRecord> fooField = new Property<SimpleFooRecord>("fooField",
+      SimpleFooRecord.class);
+
   SimpleBarRecord getBarField();
 
   Boolean getBoolField();
@@ -50,6 +53,8 @@
 
   SimpleEnum getEnumField();
 
+  SimpleFooRecord getFooField();
+
   Integer getIntId();
 
   Long getLongField();
@@ -64,6 +69,8 @@
 
   void setCreated(Date created);
 
+  void setFooField(SimpleFooRecord fooField);
+
   void setIntId(Integer intId);
 
   void setLongField(Long longField);