Revision: 8561
Author: [email protected]
Date: Wed Aug 18 03:40:09 2010
Log: Re-organized the request factory server code and implemented its first version with lots of TODOs and hacks. The instance methods now pass. The JsonRequestProcessorTest method and create/update/delete now pass.

Patch by: amitmanjhi
Review by: cromwellian

http://code.google.com/p/google-web-toolkit/source/detail?r=8561

Modified:
/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/report/ReportDetailsActivity.java /trunk/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequest.java /trunk/user/src/com/google/gwt/requestfactory/client/impl/DeltaValueStoreJsonImpl.java /trunk/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java
 /trunk/user/src/com/google/gwt/requestfactory/shared/RequestObject.java
/trunk/user/test/com/google/gwt/requestfactory/client/impl/DeltaValueStoreJsonImplTest.java /trunk/user/test/com/google/gwt/requestfactory/server/JsonRequestProcessorTest.java

=======================================
--- /trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/report/ReportDetailsActivity.java Mon Aug 16 20:22:31 2010 +++ /trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/ui/report/ReportDetailsActivity.java Wed Aug 18 03:40:09 2010
@@ -79,7 +79,6 @@
     }

RequestObject<Void> deleteRequest = requests.reportRequest().remove(view.getValue());
-    deleteRequest.delete(view.getValue());
     deleteRequest.fire(new Receiver<Void>() {

       public void onSuccess(Void ignore, Set<SyncResult> response) {
=======================================
--- /trunk/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequest.java Fri Aug 13 14:38:39 2010 +++ /trunk/user/src/com/google/gwt/requestfactory/client/impl/AbstractRequest.java Wed Aug 18 03:40:09 2010
@@ -56,10 +56,6 @@
   public void clearUsed() {
     deltaValueStore.clearUsed();
   }
-
-  public void delete(Record record) {
-    deltaValueStore.delete(record);
-  }

   @SuppressWarnings("unchecked")
   public <P extends Record> P edit(P record) {
@@ -70,7 +66,6 @@
   }

   public void fire(Receiver<T> receiver) {
-    // TODO: do something with deltaValueStore.
     assert null != receiver : "receiver cannot be null";
     this.receiver = receiver;
     requestFactory.fire(this);
=======================================
--- /trunk/user/src/com/google/gwt/requestfactory/client/impl/DeltaValueStoreJsonImpl.java Mon Aug 16 20:22:31 2010 +++ /trunk/user/src/com/google/gwt/requestfactory/client/impl/DeltaValueStoreJsonImpl.java Wed Aug 18 03:40:09 2010
@@ -56,9 +56,9 @@
     }-*/;

private static native JavaScriptObject getJsoResponse(String response) /*-{
-      // TODO: clean this
+      // TODO: clean and optimize this.
       eval("xyz=" + response);
-      return xyz;
+      return xyz["sideEffects"];
     }-*/;

     protected ReturnRecord() {
@@ -76,7 +76,17 @@
       return this.futureId;
     }-*/;

-    public final native String getId() /*-{
+    public final Long getId() {
+      String parts[] = getSchemaAndId().split("-");
+      return Long.parseLong(parts[1]);
+    }
+
+    public final String getSchema() {
+      String parts[] = getSchemaAndId().split("-");
+      return parts[0];
+    }
+
+    public final native String getSchemaAndId() /*-{
       return this.id;
     }-*/;

@@ -107,7 +117,8 @@
   // track C-U-D of CRUD operations
private final Map<RecordKey, RecordJsoImpl> creates = new HashMap<RecordKey, RecordJsoImpl>(); private final Map<RecordKey, RecordJsoImpl> updates = new HashMap<RecordKey, RecordJsoImpl>(); - private final Map<RecordKey, RecordJsoImpl> deletes = new HashMap<RecordKey, RecordJsoImpl>(); + // nothing for deletes because DeltaValueStore is not involved in deletes. The
+  // operation alone suffices.

private final Map<RecordKey, WriteOperation> operations = new HashMap<RecordKey, WriteOperation>();

@@ -153,7 +164,7 @@
         } else {
violationsMap.put(Long.valueOf(sync.getFutureId()), NULL_VIOLATIONS);
           futureToDatastoreId.put(Long.valueOf(sync.getFutureId()),
-              Long.valueOf(sync.getId()));
+              sync.getId());
         }
       }

@@ -184,33 +195,35 @@
       }
     }
     processToRemove(toRemove, WriteOperation.CREATE);
-
     toRemove.clear();
+
     if (keys.contains(WriteOperation.DELETE.name())) {
       JsArray<ReturnRecord> deletedRecords = ReturnRecord.getRecords(
           returnedJso, WriteOperation.DELETE.name());
Map<Long, Map<String, String>> violationsMap = getViolationsMap(deletedRecords); - for (Map.Entry<RecordKey, RecordJsoImpl> entry : deletes.entrySet()) {
-        final RecordKey key = entry.getKey();
+      int length = deletedRecords.length();
+ // for (Map.Entry<RecordKey, RecordJsoImpl> entry : deletes.entrySet()) {
+      for (int i = 0; i < length; i++) {
+        final RecordKey key = new RecordKey(deletedRecords.get(i).getId(),
+            requestFactory.getSchema(deletedRecords.get(i).getSchema()),
+            RequestFactoryJsonImpl.NOT_FUTURE);
         Map<String, String> violations = violationsMap.get(key.id);
         assert violations != null;
-        if (violations == NULL_VIOLATIONS) {
-          RecordJsoImpl masterRecord = master.records.get(key);
-          assert masterRecord != null;
-          master.records.remove(key);
-          toRemove.add(key);
- master.eventBus.fireEvent(masterRecord.getSchema().createChangeEvent(
-              masterRecord, WriteOperation.DELETE));
-          syncResults.add(makeSyncResult(masterRecord, null, null));
-        } else {
-          // do not change the masterRecord or fire event
- syncResults.add(makeSyncResult(entry.getValue(), violations, null));
+        RecordJsoImpl masterRecord = master.records.get(key);
+        if (masterRecord != null) {
+          if (violations == NULL_VIOLATIONS) {
+            master.records.remove(key);
+ master.eventBus.fireEvent(masterRecord.getSchema().createChangeEvent(
+                masterRecord, WriteOperation.DELETE));
+            syncResults.add(makeSyncResult(masterRecord, null, null));
+          } else {
+            // do not change the masterRecord or fire event
+ syncResults.add(makeSyncResult(masterRecord, violations, null));
+          }
         }
       }
     }
-    processToRemove(toRemove, WriteOperation.DELETE);
-
-    toRemove.clear();
+
     if (keys.contains(WriteOperation.UPDATE.name())) {
       JsArray<ReturnRecord> updatedRecords = ReturnRecord.getRecords(
           returnedJso, WriteOperation.UPDATE.name());
@@ -236,49 +249,6 @@
     processToRemove(toRemove, WriteOperation.UPDATE);
     return syncResults;
   }
-
-  public void delete(Record record) {
-    checkArgumentsAndState(record, "delete");
-    RecordImpl recordImpl = (RecordImpl) record;
-    RecordKey recordKey = new RecordKey(recordImpl);
-    RecordJsoImpl rawMasterRecord = master.records.get(recordKey);
-    if (rawMasterRecord == null) {
-      // it was a create on RF
-      RecordJsoImpl oldRecord = requestFactory.creates.remove(recordKey);
-      assert oldRecord != null;
-      return;
-    }
-    WriteOperation priorOperation = operations.get(recordKey);
-    if (priorOperation == null) {
-      operations.put(recordKey, WriteOperation.DELETE);
-      deletes.put(recordKey, recordImpl.asJso());
-      return;
-    }
-    Record priorRecord = null;
-    switch (priorOperation) {
-      case CREATE:
-        priorRecord = creates.remove(recordKey);
-        assert priorRecord != null;
-        operations.remove(recordKey);
-        break;
-      case DELETE:
-        // nothing to do here.
-        break;
-      case UPDATE:
-        // undo update
-        priorRecord = updates.remove(recordKey);
-        assert priorRecord != null;
-        operations.remove(recordKey);
-
-        // actually delete
-        operations.put(recordKey, WriteOperation.DELETE);
-        deletes.put(recordKey, recordImpl.asJso());
-        break;
-      default:
-        throw new IllegalStateException("unknown prior WriteOperation "
-            + priorOperation.name());
-    }
-  }

   public boolean isChanged() {
     assert !used;
@@ -313,15 +283,6 @@
         assert priorRecord != null;
         priorRecord.set(property, value);
         break;
-      case DELETE:
-        // undo delete
-        RecordJsoImpl recordJsoImpl = deletes.remove(recordKey);
-        assert recordJsoImpl != null;
-        operations.remove(recordKey);
-
-        // add new change record
-        addNewChangeRecord(recordKey, recordImpl, property, value);
-        break;
       case UPDATE:
         priorRecord = updates.get(recordKey);
         assert priorRecord != null;
@@ -371,7 +332,8 @@
   String toJsonWithoutChecks() {
     used = true;
     StringBuffer jsonData = new StringBuffer("{");
-    for (WriteOperation writeOperation : WriteOperation.values()) {
+    for (WriteOperation writeOperation : new WriteOperation[] {
+        WriteOperation.CREATE, WriteOperation.UPDATE}) {
       String jsonDataForOperation = getJsonForOperation(writeOperation);
       if (jsonDataForOperation.equals("")) {
         continue;
@@ -445,8 +407,6 @@
     switch (writeOperation) {
       case CREATE:
         return creates;
-      case DELETE:
-        return deletes;
       case UPDATE:
         return updates;
       default:
@@ -468,7 +428,7 @@
       } else {
         violations = NULL_VIOLATIONS;
       }
-      violationsMap.put(Long.valueOf(record.getId()), violations);
+      violationsMap.put(record.getId(), violations);
     }
     return violationsMap;
   }
@@ -513,16 +473,10 @@
       WriteOperation writeOperation) {
     for (RecordKey recordKey : toRemove) {
       operations.remove(recordKey);
-      switch (writeOperation) {
-        case CREATE:
-          creates.remove(recordKey);
-          break;
-        case DELETE:
-          deletes.remove(recordKey);
-          break;
-        case UPDATE:
-          updates.remove(recordKey);
-          break;
+      if (writeOperation == WriteOperation.CREATE) {
+        creates.remove(recordKey);
+      } else if (writeOperation == WriteOperation.UPDATE) {
+        updates.remove(recordKey);
       }
     }
   }
=======================================
--- /trunk/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java Tue Aug 17 09:57:01 2010 +++ /trunk/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java Wed Aug 18 03:40:09 2010
@@ -54,23 +54,27 @@
 public class JsonRequestProcessor implements RequestProcessor<String> {

   // TODO should we consume String, InputStream, or JSONObject?
+  private class DvsData {
+    private final JSONObject jsonObject;
+    private final WriteOperation writeOperation;
+
+    DvsData(JSONObject jsonObject, WriteOperation writeOperation) {
+      this.jsonObject = jsonObject;
+      this.writeOperation = writeOperation;
+    }
+  }

   private class EntityData {
-    private final EntityKey entityKey;
     private final Object entityInstance;
     // TODO: violations should have more structure than JSONObject
     private final JSONObject violations;
     private final WriteOperation writeOperation;
-    private final Class<?> entityClass;
-
-    EntityData(EntityKey entityKey, Object entityInstance,
-        JSONObject violations, WriteOperation writeOperation,
-        Class<?> entityClass) {
-      this.entityKey = entityKey;
+
+    EntityData(Object entityInstance, JSONObject violations,
+        WriteOperation writeOperation) {
       this.entityInstance = entityInstance;
       this.violations = violations;
       this.writeOperation = writeOperation;
-      this.entityClass = entityClass;
     }
   }

@@ -122,7 +126,20 @@

   private OperationRegistry operationRegistry;

-  private Map<EntityKey, EntityData> entityDataMap;
+  /*
+   * <li>Request comes in. Construct the involvedKeys, dvsDataMap and
+   * beforeDataMap, using DVS and parameters.
+   *
+   * <li>Apply the DVS and construct the afterDvsDataMqp.
+   *
+   * <li>Invoke the method noted in the operation.
+   *
+   * <li>Find the changes that need to be sent back.
+   */
+  private Set<EntityKey> involvedKeys = new HashSet<EntityKey>();
+ private Map<EntityKey, DvsData> dvsDataMap = new HashMap<EntityKey, DvsData>(); + private Map<EntityKey, EntityData> beforeDataMap = new HashMap<EntityKey, EntityData>(); + private Map<EntityKey, EntityData> afterDvsDataMap = new HashMap<EntityKey, EntityData>();

public Collection<Property<?>> allProperties(Class<? extends Record> clazz) {
     Set<Property<?>> rtn = new HashSet<Property<?>>();
@@ -238,6 +255,13 @@
       return new Date(Long.parseLong(parameterValue));
     }
     if (Record.class.isAssignableFrom(parameterType)) {
+ /* TODO: 1. Don't resolve in this step, just get EntityKey. May need to
+       * use DVS.
+       *
+ * 2. Merge the following and the object resolution code in getEntityKey.
+       * 3. Update the involvedKeys set.
+       */
+      // TODO(amitmanjhi): shouldn't this be DataTransferObject.class?
       Service service = parameterType.getAnnotation(Service.class);
       if (service != null) {
         Class<?> sClass = service.value();
@@ -309,9 +333,9 @@
       Method idMethod = returnValue.getClass().getMethod("getId");
       Long id = (Long) idMethod.invoke(returnValue);

-      String keyRef =
- operationRegistry.getSecurityProvider().encodeClassType(propertyType)
-              + "-" + id;
+ String keyRef = operationRegistry.getSecurityProvider().encodeClassType(
+          propertyType)
+          + "-" + id;
       addRelatedObject(keyRef, returnValue,
           (Class<? extends Record>) propertyType,
           propertyContext.getProperty(propertyName));
@@ -338,15 +362,13 @@
    * <p>
    * A <i>set</i> might have side-effects, but we don't handle that.
    */
-  public EntityData getEntityDataForRecord(String recordToken,
+  public EntityData getEntityDataForRecord(EntityKey entityKey,
JSONObject recordObject, WriteOperation writeOperation) throws JSONException {
-    Class<? extends Record> record = getRecordFromClassToken(recordToken);
-    EntityKey entityKey = new EntityKey(recordObject.getLong("id"),
-        (writeOperation == WriteOperation.CREATE), record);
+
     try {
-      Class<?> entity = getEntityFromRecordAnnotation(record);
-
- Map<String, Class<?>> propertiesInRecord = getPropertiesFromRecord(record);
+      Class<?> entity = getEntityFromRecordAnnotation(entityKey.record);
+
+ Map<String, Class<?>> propertiesInRecord = getPropertiesFromRecord(entityKey.record);
       validateKeys(recordObject, propertiesInRecord.keySet());
       updatePropertyTypes(propertiesInRecord, entity);

@@ -392,9 +414,8 @@
       if (validator != null) {
         violations = validator.validate(entityInstance);
       }
-
-      return new EntityData(entityKey, entityInstance,
-          getViolationsAsJson(violations), writeOperation, entity);
+      return new EntityData(entityInstance, (violations.isEmpty() ? null
+          : getViolationsAsJson(violations)), writeOperation);
     } catch (Exception ex) {
       log.severe(String.format("Caught exception [%s] %s",
           ex.getClass().getName(), ex.getLocalizedMessage()));
@@ -486,8 +507,8 @@
   }

   /**
-   * Returns Object[0][0] as the object instance or null if it is a static
-   * method. Returns Object[1] as the params array.
+ * Returns Object[0][0] as the entityKey corresponding to the object instance + * or null if it is a static method. Returns Object[1] as the params array.
    */
   public Object[][] getObjectsFromParameterMap(boolean isInstanceMethod,
       Map<String, String> parameterMap, Class<?> parameterClasses[]) {
@@ -496,10 +517,14 @@
     Object args[][] = new Object[2][];
     args[0] = new Object[1];
     if (isInstanceMethod) {
- args[0][0] = getEntityInstance(parameterMap.get(RequestData.PARAM_TOKEN + "0")); + EntityKey entityKey = getEntityKey(parameterMap.get(RequestData.PARAM_TOKEN + "0"));
+      involvedKeys.add(entityKey);
+      args[0][0] = entityKey;
     } else {
       args[0][0] = null;
     }
+
+    // TODO: update the involvedKeys for other params
     int offset = (isInstanceMethod ? 1 : 0);
     args[1] = new Object[parameterClasses.length];
     for (int i = 0; i < parameterClasses.length; i++) {
@@ -577,44 +602,6 @@
           "Non-existent record class " + recordToken);
     }
   }
-
-  public JSONObject getReturnRecord(WriteOperation writeOperation,
-      Object entityInstance, JSONObject recordObject,
-      Set<ConstraintViolation<Object>> violations,
-      Class<? extends Record> record)
-      throws SecurityException, JSONException, IllegalAccessException,
-      InvocationTargetException, NoSuchMethodException {
- // id/futureId, the identifying field is sent back from the incoming record.
-    JSONObject returnObject = new JSONObject();
- final boolean hasViolations = violations != null && !violations.isEmpty();
-    if (hasViolations) {
-      returnObject.put("violations", getViolationsAsJson(violations));
-    }
-    switch (writeOperation) {
-      case CREATE:
-        returnObject.put("futureId", recordObject.getString("id"));
-        if (!hasViolations) {
-          returnObject.put("id",
-              encodePropertyValueFromDataStore(entityInstance, Long.class,
-                  "id", propertyRefs));
-          returnObject.put("version",
- encodePropertyValueFromDataStore(entityInstance, Integer.class,
-                  "version", propertyRefs));
-        }
-        break;
-      case DELETE:
-        returnObject.put("id", recordObject.getString("id"));
-        break;
-      case UPDATE:
-        returnObject.put("id", recordObject.getString("id"));
-        if (!hasViolations) {
-          returnObject.put("version", encodePropertyValueFromDataStore(
-              entityInstance, Integer.class, "version", propertyRefs));
-        }
-        break;
-    }
-    return returnObject;
-  }

   public JSONObject getViolationsAsJson(
       Set<ConstraintViolation<Object>> violations) throws JSONException {
@@ -651,16 +638,32 @@
+ " should " + (operation.isInstance() ? "not " : "") + "be static");
     }

-    entityDataMap = topLevelJsonObject.has(RequestData.CONTENT_TOKEN)
- ? decodeDVS(topLevelJsonObject.getString(RequestData.CONTENT_TOKEN))
-        : new HashMap<EntityKey, EntityData>();
-
+    if (topLevelJsonObject.has(RequestData.CONTENT_TOKEN)) {
+       // updates involvedKeys and dvsDataMap.
+       decodeDVS(topLevelJsonObject.getString(RequestData.CONTENT_TOKEN));
+    }
    // get the domain object (for instance methods) and args.
     Object args[][] = getObjectsFromParameterMap(operation.isInstance(),
getParameterMap(topLevelJsonObject), domainMethod.getParameterTypes());
-    Object result = invokeDomainMethod(args[0][0], domainMethod, args[1]);
-
-    // TODO: Need to do snap-shotting and compare before and after.
+
+    // Construct beforeDataMap
+    constructBeforeDataMap();
+    // Construct afterDvsDataMap.
+    constructAfterDvsDataMap();
+
+    // resolve parameters that are so far just EntityKeys.
+    // TODO: resolve paramters other than  the domainInstance
+    EntityKey domainEntityKey = null;
+    if (args[0][0] != null) {
+      domainEntityKey = (EntityKey) args[0][0];
+      EntityData domainEntityData = afterDvsDataMap.get(domainEntityKey);
+      assert domainEntityData != null;
+      args[0][0] = domainEntityData.entityInstance;
+      assert args[0][0] != null;
+    }
+    Object result = invokeDomainMethod(args[0][0], domainMethod, args[1]);
+
+    JSONObject sideEffects = getSideEffects();

     if ((result instanceof List<?>) != operation.isReturnTypeList()) {
       throw new IllegalArgumentException(
@@ -674,6 +677,7 @@
       JSONObject envelop = new JSONObject();
       envelop.put("result", toJsonArray(operation, result));
       envelop.put("related", encodeRelatedObjectsToJson());
+      envelop.put("sideEffects", sideEffects);
       return envelop;
     } else if (result instanceof Number
&& !(result instanceof BigDecimal || result instanceof BigInteger)) {
@@ -683,6 +687,7 @@
       JSONObject jsonObject = toJsonObject(operation, result);
       envelop.put("result", jsonObject);
       envelop.put("related", encodeRelatedObjectsToJson());
+      envelop.put("sideEffects", sideEffects);
       return envelop;
     }
   }
@@ -714,12 +719,68 @@
   }

   /**
-   * Decode deltaValueStore.
+   * @throws JSONException
+   *
    */
-  private Map<EntityKey, EntityData> decodeDVS(String content)
-      throws SecurityException {
-
- Map<EntityKey, EntityData> returnMap = new HashMap<EntityKey, EntityData>();
+  private void constructAfterDvsDataMap() throws JSONException {
+    afterDvsDataMap = new HashMap<EntityKey, EntityData>();
+    for (EntityKey entityKey : involvedKeys) {
+      // use the beforeDataMap and dvsDataMap
+      DvsData dvsData = dvsDataMap.get(entityKey);
+      if (dvsData != null) {
+        EntityData entityData = getEntityDataForRecord(entityKey,
+            dvsData.jsonObject, dvsData.writeOperation);
+        if (entityKey.isFuture) {
+          // TODO: assert that the id is null for entityData.entityInstance
+        }
+        afterDvsDataMap.put(entityKey, entityData);
+      } else {
+        assert !entityKey.isFuture;
+        EntityData entityData = beforeDataMap.get(entityKey);
+        assert entityData != null;
+        // TODO(cromwellian): copy here
+        afterDvsDataMap.put(entityKey, entityData);
+      }
+    }
+  }
+
+  /**
+   * Constructs the beforeDataMap.
+   *
+   * <p>
+   * Algorithm: go through the involvedKeys, and find the entityData
+   * corresponding to each.
+   *
+   * @throws NoSuchMethodException
+   * @throws InvocationTargetException
+   * @throws IllegalAccessException
+   * @throws SecurityException
+   * @throws IllegalArgumentException
+   */
+  private void constructBeforeDataMap() throws IllegalArgumentException,
+      SecurityException, IllegalAccessException, InvocationTargetException,
+      NoSuchMethodException {
+    beforeDataMap = new HashMap<EntityKey, EntityData>();
+    for (EntityKey entityKey : involvedKeys) {
+      if (entityKey.isFuture) {
+        // the "before" is empty.
+        continue;
+      }
+      //
+ Class<?> entityClass = getEntityFromRecordAnnotation(entityKey.record);
+      // TODO: merge this lookup code with other uses.
+      Object entityInstance = entityClass.getMethod(
+          "find" + entityClass.getSimpleName(), Long.class).invoke(null,
+          new Long(entityKey.id));
+ beforeDataMap.put(entityKey, new EntityData(entityInstance, null, null));
+    }
+  }
+
+  /**
+   * Decode deltaValueStore to populate involvedKeys and dvsDataMap.
+   */
+  private void decodeDVS(String content)
+      throws SecurityException {
     try {
       JSONObject jsonObject = new JSONObject(content);
       for (WriteOperation writeOperation : WriteOperation.values()) {
@@ -742,16 +803,41 @@
                 "There cannot be more than one record token");
           }
JSONObject recordObject = recordWithSchema.getJSONObject(recordToken);
-          EntityData entityData = getEntityDataForRecord(recordToken,
-              recordObject, writeOperation);
-          returnMap.put(entityData.entityKey, entityData);
+ Class<? extends Record> record = getRecordFromClassToken(recordToken);
+          EntityKey entityKey = new EntityKey(recordObject.getLong("id"),
+              (writeOperation == WriteOperation.CREATE), record);
+          involvedKeys.add(entityKey);
+ dvsDataMap.put(entityKey, new DvsData(recordObject, writeOperation));
         }
       }
-      return returnMap;
     } catch (JSONException e) {
       throw new IllegalArgumentException("sync failed: ", e);
     }
   }
+
+  private WriteOperation detectDeleteOrUpdate(EntityKey entityKey,
+      EntityData entityData) throws IllegalArgumentException,
+      SecurityException, IllegalAccessException, InvocationTargetException,
+      NoSuchMethodException {
+    if (entityData.entityInstance == null) {
+      return null;
+    }
+
+    Class<?> entityClass = getEntityFromRecordAnnotation(entityKey.record);
+    // TODO: merge this lookup code with other uses.
+    Object entityInstance = entityClass.getMethod(
+        "find" + entityClass.getSimpleName(), Long.class).invoke(null,
+        new Long(entityKey.id));
+    if (entityInstance == null) {
+      return WriteOperation.DELETE;
+    }
+    // TODO (cromwellian): is it an update? how to detect?
+    // This is a HACK!!!
+    if (entityData.writeOperation == WriteOperation.UPDATE) {
+      return WriteOperation.UPDATE;
+    }
+    return null;
+  }

   private JSONObject encodeRelatedObjectsToJson() throws JSONException {
     JSONObject array = new JSONObject();
@@ -760,6 +846,34 @@
     }
     return array;
   }
+
+  private JSONObject getCreateReturnRecord(EntityKey originalEntityKey,
+      EntityData entityData) throws SecurityException, JSONException,
+ IllegalAccessException, InvocationTargetException, NoSuchMethodException { + // id/futureId, the identifying field is sent back from the incoming record.
+    assert originalEntityKey.isFuture;
+    Object entityInstance = entityData.entityInstance;
+    assert entityInstance != null;
+    JSONObject returnObject = new JSONObject();
+    returnObject.put("futureId", originalEntityKey.id + "");
+    boolean hasViolations = entityData.violations != null
+        && entityData.violations.length() > 0;
+    if (hasViolations) {
+      returnObject.put("violations", entityData.violations);
+    } else {
+      Object newId = encodePropertyValueFromDataStore(entityInstance,
+          Long.class, "id", propertyRefs);
+      if (newId == null) {
+        log.warning("Record with futureId " + originalEntityKey.id
+            + " not persisted");
+        return null; // no changeRecord for this CREATE.
+      }
+ returnObject.put("id", getSchemaAndId(originalEntityKey.record, newId));
+      returnObject.put("version", encodePropertyValueFromDataStore(
+          entityInstance, Integer.class, "version", propertyRefs));
+    }
+    return returnObject;
+  }

   private EntityData getEntityDataForException(EntityKey entityKey,
       WriteOperation writeOperation, Exception ex) {
@@ -777,25 +891,71 @@
       // ignore.
       e.printStackTrace();
     }
- return new EntityData(entityKey, null, violations, writeOperation, null);
+    return new EntityData(null, violations, writeOperation);
   }

   /**
-   * Given param0, decodeId and FutureId. String is of the form "239-NO" or
-   * "239-IS". the latter is a futureId
+   * Given param0, return the EntityKey. String is of the form
+   * "239-NO-com....EmployeeRecord" or "239-IS-com...EmployeeRecord".
    */
-  private Object getEntityInstance(String string) {
+  private EntityKey getEntityKey(String string) {
     String parts[] = string.split("-");
     assert parts.length == 3;

     Long id = Long.parseLong(parts[0]);
-    EntityKey entityKey = new EntityKey(id, "IS".equals(parts[1]),
+    return new EntityKey(id, "IS".equals(parts[1]),
         getRecordFromClassToken(parts[2]));
-    EntityData entityData = entityDataMap.get(entityKey);
-    assert entityData != null;
-    return entityData.entityInstance;
   }

+ private String getSchemaAndId(Class<? extends Record> record, Object newId) {
+    return record.getName() + "-" + newId;
+  }
+
+  /**
+ * Returns a JSONObject with at most three keys: CREATE, UPDATE, DELETE. Each
+   * value is a JSONArray of JSONObjects.
+   */
+ private JSONObject getSideEffects() throws SecurityException, JSONException, + IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+    JSONObject sideEffects = new JSONObject();
+    JSONArray createArray = new JSONArray();
+    JSONArray deleteArray = new JSONArray();
+    JSONArray updateArray = new JSONArray();
+    for (EntityKey entityKey : involvedKeys) {
+      EntityData entityData = afterDvsDataMap.get(entityKey);
+      assert entityData != null;
+      if (entityKey.isFuture) {
+ JSONObject createRecord = getCreateReturnRecord(entityKey, entityData);
+        if (createRecord != null) {
+          createArray.put(createRecord);
+        }
+        continue;
+      }
+      WriteOperation writeOperation = detectDeleteOrUpdate(entityKey,
+          entityData);
+      if (writeOperation == WriteOperation.DELETE) {
+        JSONObject deleteRecord = new JSONObject();
+ deleteRecord.put("id", getSchemaAndId(entityKey.record, entityKey.id));
+        deleteArray.put(deleteRecord);
+      }
+      if (writeOperation == WriteOperation.UPDATE) {
+        JSONObject updateRecord = new JSONObject();
+ updateRecord.put("id", getSchemaAndId(entityKey.record, entityKey.id));
+        updateArray.put(updateRecord);
+      }
+    }
+    if (createArray.length() > 0) {
+      sideEffects.put(WriteOperation.CREATE.name(), createArray);
+    }
+    if (deleteArray.length() > 0) {
+      sideEffects.put(WriteOperation.DELETE.name(), deleteArray);
+    }
+    if (updateArray.length() > 0) {
+      sideEffects.put(WriteOperation.UPDATE.name(), updateArray);
+    }
+    return sideEffects;
+  }
+
   /**
* returns true if the property has been requested. TODO: use the properties
    * that should be coming with the request.
=======================================
--- /trunk/user/src/com/google/gwt/requestfactory/shared/RequestObject.java Tue Aug 10 18:34:06 2010 +++ /trunk/user/src/com/google/gwt/requestfactory/shared/RequestObject.java Wed Aug 18 03:40:09 2010
@@ -34,10 +34,6 @@
    */
   void clearUsed();

- // TODO: temporary hack so that I could get rid of DeltaValueStore. This will
-  // be removed once the hack for SYNC requests goes away.
-  void delete(Record record);
-
   <P extends Record> P edit(P record);

   void fire(Receiver<T> receiver);
=======================================
--- /trunk/user/test/com/google/gwt/requestfactory/client/impl/DeltaValueStoreJsonImplTest.java Mon Aug 16 20:22:31 2010 +++ /trunk/user/test/com/google/gwt/requestfactory/client/impl/DeltaValueStoreJsonImplTest.java Wed Aug 18 03:40:09 2010
@@ -130,18 +130,6 @@
     assertTrue(deltaValueStore.isChanged());
testAndGetChangeRecord(deltaValueStore.toJson(), WriteOperation.CREATE);
   }
-
-  public void testCreateDelete() {
-    Record created = requestFactory.create(SimpleFooRecord.class);
-    DeltaValueStoreJsonImpl deltaValueStore = new DeltaValueStoreJsonImpl(
-        valueStore, requestFactory);
-    assertFalse(deltaValueStore.isChanged());
-    deltaValueStore.delete(created);
-    assertFalse(deltaValueStore.isChanged());
-
-    String jsonString = deltaValueStore.toJson();
-    assertEquals("{}", jsonString);
-  }

   public void testCreateUpdate() {
     Record created = requestFactory.create(SimpleFooRecord.class);
@@ -158,36 +146,12 @@
changeRecord.get(SimpleFooRecord.userName.getName()).isString().stringValue());
   }

-  public void testDelete() {
+  public void testOperationAfterJson() {
     DeltaValueStoreJsonImpl deltaValueStore = new DeltaValueStoreJsonImpl(
         valueStore, requestFactory);
-    deltaValueStore.delete(new MyRecordImpl(jso));
-    assertTrue(deltaValueStore.isChanged());
- testAndGetChangeRecord(deltaValueStore.toJson(), WriteOperation.DELETE);
-  }
-
-  public void testDeleteUpdate() {
-    DeltaValueStoreJsonImpl deltaValueStore = new DeltaValueStoreJsonImpl(
-        valueStore, requestFactory);
-    deltaValueStore.delete(new MyRecordImpl(jso));
-    assertTrue(deltaValueStore.isChanged());
-
-    // update after a delete nullifies the delete.
     deltaValueStore.set(SimpleFooRecord.userName, new MyRecordImpl(jso),
-        "harry");
+        "newHarry");
     assertTrue(deltaValueStore.isChanged());
- JSONObject changeRecord = testAndGetChangeRecord(deltaValueStore.toJson(),
-        WriteOperation.UPDATE);
-    assertEquals(
-        "harry",
- changeRecord.get(SimpleFooRecord.userName.getName()).isString().stringValue());
-   }
-
-  public void testOperationAfterJson() {
-    DeltaValueStoreJsonImpl deltaValueStore = new DeltaValueStoreJsonImpl(
-        valueStore, requestFactory);
-    deltaValueStore.delete(new MyRecordImpl(jso));
-    assertTrue(deltaValueStore.isChanged());

     deltaValueStore.toJson();

@@ -251,19 +215,6 @@
         "harry",
changeRecord.get(SimpleFooRecord.userName.getName()).isString().stringValue());
   }
-
-  public void testUpdateDelete() {
-    DeltaValueStoreJsonImpl deltaValueStore = new DeltaValueStoreJsonImpl(
-        valueStore, requestFactory);
-    deltaValueStore.set(SimpleFooRecord.userName, new MyRecordImpl(jso),
-        "harry");
-    assertTrue(deltaValueStore.isChanged());
-
-    // delete after an update nullifies the delete.
-    deltaValueStore.delete(new MyRecordImpl(jso));
-    assertTrue(deltaValueStore.isChanged());
- testAndGetChangeRecord(deltaValueStore.toJson(), WriteOperation.DELETE);
-  }

   private JSONObject testAndGetChangeRecord(String jsonString,
       WriteOperation currentWriteOperation) {
=======================================
--- /trunk/user/test/com/google/gwt/requestfactory/server/JsonRequestProcessorTest.java Tue Aug 17 09:57:01 2010 +++ /trunk/user/test/com/google/gwt/requestfactory/server/JsonRequestProcessorTest.java Wed Aug 18 03:40:09 2010
@@ -16,6 +16,7 @@
 package com.google.gwt.requestfactory.server;

 import com.google.gwt.requestfactory.shared.RequestData;
+import com.google.gwt.valuestore.server.SimpleFoo;
 import com.google.gwt.valuestore.shared.SimpleEnum;
 import com.google.gwt.valuestore.shared.SimpleFooRecord;
 import com.google.gwt.valuestore.shared.WriteOperation;
@@ -133,16 +134,16 @@

       // TODO: commented till snapshotting and automatic-diff is ready.
       // check modified fields and no violations
-//      SimpleFoo fooResult = SimpleFoo.getSingleton();
-//      assertFalse(result.getJSONArray("UPDATE").getJSONObject(0).has(
-//          "violations"));
-//      assertEquals((int) 45, (int) fooResult.getIntId());
-//      assertEquals("JSC", fooResult.getUserName());
-//      assertEquals(now, fooResult.getCreated());
-//      assertEquals(9L, (long) fooResult.getLongField());
-//      assertEquals(com.google.gwt.valuestore.shared.SimpleEnum.BAR,
-//          fooResult.getEnumField());
-//      assertEquals(false, (boolean) fooResult.getBoolField());
+      SimpleFoo fooResult = SimpleFoo.getSingleton();
+ assertFalse(result.getJSONObject("sideEffects").getJSONArray("UPDATE").getJSONObject(
+          0).has("violations"));
+      assertEquals((int) 45, (int) fooResult.getIntId());
+      assertEquals("JSC", fooResult.getUserName());
+      assertEquals(now, fooResult.getCreated());
+      assertEquals(9L, (long) fooResult.getLongField());
+      assertEquals(com.google.gwt.valuestore.shared.SimpleEnum.BAR,
+          fooResult.getEnumField());
+      assertEquals(false, (boolean) fooResult.getBoolField());

     } catch (Exception e) {
       e.printStackTrace();

--
http://groups.google.com/group/Google-Web-Toolkit-Contributors

Reply via email to