Revision: 8556
Author: [email protected]
Date: Tue Aug 17 09:57:01 2010
Log: Preliminary implementation of the server-side sync method. When a record is to
be passed as a parameter, we now pass its uniqueId().

The correct methods on the server are being invoked. However, the snapshotting and change detection are not done. So there will be exceptions on the client.

Review by: [email protected]
http://code.google.com/p/google-web-toolkit/source/detail?r=8556

Deleted:
 /trunk/user/test/com/google/gwt/requestfactory/server/SimpleFooRequest.java
Modified:
/trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpensesMobile.java
 /trunk/user/src/com/google/gwt/requestfactory/client/impl/RecordImpl.java
/trunk/user/src/com/google/gwt/requestfactory/rebind/RequestFactoryGenerator.java /trunk/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java /trunk/user/src/com/google/gwt/requestfactory/server/ReflectionBasedOperationRegistry.java
 /trunk/user/src/com/google/gwt/requestfactory/server/RequestDefinition.java
/trunk/user/src/com/google/gwt/requestfactory/server/SampleDataPopulator.java
 /trunk/user/src/com/google/gwt/requestfactory/shared/RequestData.java
 /trunk/user/src/com/google/gwt/requestfactory/shared/RequestFactory.java
/trunk/user/test/com/google/gwt/requestfactory/server/JsonRequestProcessorTest.java
 /trunk/user/test/com/google/gwt/valuestore/shared/SimpleFooRequest.java

=======================================
--- /trunk/user/test/com/google/gwt/requestfactory/server/SimpleFooRequest.java Fri Jul 30 11:32:15 2010
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.requestfactory.server;
-
-import com.google.gwt.requestfactory.shared.RecordListRequest;
-import com.google.gwt.requestfactory.shared.RequestObject;
-import com.google.gwt.requestfactory.shared.Service;
-import com.google.gwt.valuestore.shared.SimpleFooRecord;
-
-/**
- * Do nothing test interface.
- */
-...@service(com.google.gwt.requestfactory.server.SimpleFoo.class)
-public interface SimpleFooRequest {
-  RequestObject<Long> countSimpleFoo();
-  RecordListRequest<SimpleFooRecord> findAll();
-  RequestObject<SimpleFooRecord> findSimpleFooById(Long id);
-  RequestObject<Integer> privateMethod();
-}
=======================================
--- /trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpensesMobile.java Mon Aug 16 06:28:43 2010 +++ /trunk/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpensesMobile.java Tue Aug 17 09:57:01 2010
@@ -95,7 +95,6 @@
         ExpensesRequestFactory.class);
     requestFactory.init(eventBus);

-    final long finalEmployeeId = employeeId;
requestFactory.employeeRequest().findEmployee(Value.of(employeeId)).fire(
         new Receiver<EmployeeRecord>() {
           public void onSuccess(EmployeeRecord employee,
=======================================
--- /trunk/user/src/com/google/gwt/requestfactory/client/impl/RecordImpl.java Mon Aug 16 20:22:31 2010 +++ /trunk/user/src/com/google/gwt/requestfactory/client/impl/RecordImpl.java Tue Aug 17 09:57:01 2010
@@ -62,6 +62,11 @@
   public RecordSchema<?> getSchema() {
     return jso.getSchema();
   }
+
+  public String getUniqueId() {
+    return jso.getId() + "-" + (isFuture ? "IS" : "NO") + "-"
+        + getSchema().getToken().getName();
+  }

   public Integer getVersion() {
     return jso.getVersion();
=======================================
--- /trunk/user/src/com/google/gwt/requestfactory/rebind/RequestFactoryGenerator.java Mon Aug 16 20:22:31 2010 +++ /trunk/user/src/com/google/gwt/requestfactory/rebind/RequestFactoryGenerator.java Tue Aug 17 09:57:01 2010
@@ -638,7 +638,8 @@
       JClassType classType = parameter.getType().isClassOrInterface();
       if (classType != null
&& classType.isAssignableTo(typeOracle.findType(Record.class.getName()))) {
-        sb.append(".getId()");
+ sb.insert(0, "((" + classType.getQualifiedBinaryName() + "Impl" + ")");
+        sb.append(").getUniqueId()");
       }
     }
     return "new Object[] {" + sb.toString() + "}";
=======================================
--- /trunk/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java Tue Aug 17 10:06:27 2010 +++ /trunk/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java Tue Aug 17 09:57:01 2010
@@ -17,7 +17,6 @@

 import com.google.gwt.requestfactory.shared.DataTransferObject;
 import com.google.gwt.requestfactory.shared.RequestData;
-import com.google.gwt.requestfactory.shared.RequestFactory;
 import com.google.gwt.requestfactory.shared.Service;
 import com.google.gwt.valuestore.shared.Property;
 import com.google.gwt.valuestore.shared.Record;
@@ -54,28 +53,61 @@
  */
 public class JsonRequestProcessor implements RequestProcessor<String> {

- private static final Logger log = Logger.getLogger(JsonRequestProcessor.class.getName());
-
   // TODO should we consume String, InputStream, or JSONObject?

-  /**
-   * A class representing the pair of a domain entity and its corresponding
-   * record class on the client side.
-   */
-  public static class EntityRecordPair {
-
-    public final Class<?> entity;
-
-    public final Class<? extends Record> record;
-
-    EntityRecordPair(Class<?> entity, Class<? extends Record> record) {
-      this.entity = entity;
+  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;
+      this.entityInstance = entityInstance;
+      this.violations = violations;
+      this.writeOperation = writeOperation;
+      this.entityClass = entityClass;
+    }
+  }
+
+  private class EntityKey {
+    private final boolean isFuture;
+    // TODO: update for non-long id?
+    private final long id;
+    private final Class<? extends Record> record;
+
+    EntityKey(long id, boolean isFuture, Class<? extends Record> record) {
+      this.id = id;
+      this.isFuture = isFuture;
+      assert record != null;
       this.record = record;
     }
+
+    @Override
+    public boolean equals(Object ob) {
+      if (!(ob instanceof EntityKey)) {
+        return false;
+      }
+      EntityKey other = (EntityKey) ob;
+      return (id == other.id) && (isFuture == other.isFuture)
+          && (record.equals(other.record));
+    }
+
+    @Override
+    public int hashCode() {
+ return (int) (31 * this.record.hashCode() + (31 * this.id + (isFuture ? 1
+          : 0)));
+    }
   }

   public static final Set<String> BLACK_LIST = initBlackList();

+ private static final Logger log = Logger.getLogger(JsonRequestProcessor.class.getName());
+
   public static Set<String> initBlackList() {
     Set<String> blackList = new HashSet<String>();
     for (String str : new String[] {"password"}) {
@@ -86,15 +118,17 @@

   private RequestProperty propertyRefs;

-  private Map<String, JSONObject> relatedObjects
-      = new HashMap<String, JSONObject>();
+ private Map<String, JSONObject> relatedObjects = new HashMap<String, JSONObject>();

   private OperationRegistry operationRegistry;

+  private Map<EntityKey, EntityData> entityDataMap;
+
public Collection<Property<?>> allProperties(Class<? extends Record> clazz) {
     Set<Property<?>> rtn = new HashSet<Property<?>>();
     for (Field f : clazz.getFields()) {
- if (Modifier.isStatic(f.getModifiers()) && Property.class.isAssignableFrom(f.getType())) {
+      if (Modifier.isStatic(f.getModifiers())
+          && Property.class.isAssignableFrom(f.getType())) {
         try {
           rtn.add((Property<?>) f.get(null));
         } catch (IllegalAccessException e) {
@@ -298,6 +332,75 @@
     // ignored. id is assigned by default.
     return null;
   }
+
+  /**
+   * Returns the entityData for a record in the DeltaValueStore.
+   * <p>
+   * A <i>set</i> might have side-effects, but we don't handle that.
+   */
+  public EntityData getEntityDataForRecord(String recordToken,
+ 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);
+      validateKeys(recordObject, propertiesInRecord.keySet());
+      updatePropertyTypes(propertiesInRecord, entity);
+
+      // get entityInstance
+      Object entityInstance = getEntityInstance(writeOperation, entity,
+          recordObject.get("id"), propertiesInRecord.get("id"));
+
+      Set<ConstraintViolation<Object>> violations = Collections.emptySet();
+      Iterator<?> keys = recordObject.keys();
+      while (keys.hasNext()) {
+        String key = (String) keys.next();
+        Class<?> propertyType = propertiesInRecord.get(key);
+ if (writeOperation == WriteOperation.CREATE && ("id".equals(key))) {
+          Long id = generateIdForCreate(key);
+          if (id != null) {
+            entity.getMethod(getMethodNameFromPropertyName(key, "set"),
+                propertyType).invoke(entityInstance, id);
+          }
+        } else {
+ Object propertyValue = getPropertyValueFromRequest(recordObject, key,
+              propertyType);
+          entity.getMethod(getMethodNameFromPropertyName(key, "set"),
+              propertyType).invoke(entityInstance, propertyValue);
+        }
+      }
+
+      // validations check..
+      Validator validator = null;
+      try {
+ ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
+        validator = validatorFactory.getValidator();
+      } catch (Exception e) {
+        /*
+ * This is JBoss's clumsy way of telling us that the system has not been
+         * configured.
+         */
+        log.info(String.format(
+ "Ingnoring exception caught initializing bean validation framework. "
+                + "It is probably unconfigured or misconfigured. [%s] %s ",
+            e.getClass().getName(), e.getLocalizedMessage()));
+      }
+
+      if (validator != null) {
+        violations = validator.validate(entityInstance);
+      }
+
+      return new EntityData(entityKey, entityInstance,
+          getViolationsAsJson(violations), writeOperation, entity);
+    } catch (Exception ex) {
+      log.severe(String.format("Caught exception [%s] %s",
+          ex.getClass().getName(), ex.getLocalizedMessage()));
+      return getEntityDataForException(entityKey, writeOperation, ex);
+    }
+  }

   @SuppressWarnings("unchecked")
   public Class<Object> getEntityFromRecordAnnotation(
@@ -356,9 +459,8 @@

       if (requestedProperty(p, propertyContext)) {
         String propertyName = p.getName();
-        jsonObject.put(propertyName,
-            encodePropertyValueFromDataStore(entityElement, p.getType(),
-                propertyName, propertyContext));
+        jsonObject.put(propertyName, encodePropertyValueFromDataStore(
+            entityElement, p.getType(), propertyName, propertyContext));
       }
     }
     return jsonObject;
@@ -383,13 +485,26 @@
     return methodName.toString();
   }

- public Object[] getObjectsFromParameterMap(Map<String, String> parameterMap,
-      Class<?> parameterClasses[]) {
+  /**
+   * Returns Object[0][0] as 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[]) {
+    // TODO: create an EntityMethodCall (instance, args) instead.
     assert parameterClasses != null;
-    Object args[] = new Object[parameterClasses.length];
+    Object args[][] = new Object[2][];
+    args[0] = new Object[1];
+    if (isInstanceMethod) {
+ args[0][0] = getEntityInstance(parameterMap.get(RequestData.PARAM_TOKEN + "0"));
+    } else {
+      args[0][0] = null;
+    }
+    int offset = (isInstanceMethod ? 1 : 0);
+    args[1] = new Object[parameterClasses.length];
     for (int i = 0; i < parameterClasses.length; i++) {
-      args[i] = decodeParameterValue(parameterClasses[i],
-          parameterMap.get("param" + i));
+      args[1][i] = decodeParameterValue(parameterClasses[i],
+          parameterMap.get(RequestData.PARAM_TOKEN + (i + offset)));
     }
     return args;
   }
@@ -425,14 +540,13 @@
    * Returns the property fields (name => type) for a record.
    */
   public Map<String, Class<?>> getPropertiesFromRecord(
-      Class<? extends Record> record)
-      throws SecurityException, IllegalAccessException,
-      InvocationTargetException, NoSuchMethodException {
+      Class<? extends Record> record) throws SecurityException,
+ IllegalAccessException, InvocationTargetException, NoSuchMethodException {
     Map<String, Class<?>> properties = new HashMap<String, Class<?>>();
     for (Field f : record.getFields()) {
       if (Property.class.isAssignableFrom(f.getType())) {
-        Class<?> propertyType = (Class<?>) f.getType().getMethod(
-            "getType").invoke(f.get(null));
+ Class<?> propertyType = (Class<?>) f.getType().getMethod("getType").invoke(
+            f.get(null));
         properties.put(f.getName(), propertyType);
       }
     }
@@ -494,53 +608,27 @@
       case UPDATE:
         returnObject.put("id", recordObject.getString("id"));
         if (!hasViolations) {
-          returnObject.put("version",
- encodePropertyValueFromDataStore(entityInstance, Integer.class,
-                  "version", propertyRefs));
+          returnObject.put("version", encodePropertyValueFromDataStore(
+              entityInstance, Integer.class, "version", propertyRefs));
         }
         break;
     }
     return returnObject;
   }
-
- public JSONObject getReturnRecordForException(WriteOperation writeOperation,
-      JSONObject recordObject, Exception ex) {
-    JSONObject returnObject = new JSONObject();
-    try {
-      if (writeOperation == WriteOperation.DELETE
-          || writeOperation == WriteOperation.UPDATE) {
-        returnObject.put("id", recordObject.getString("id"));
-      } else {
-        returnObject.put("futureId", recordObject.getString("id"));
-      }
-      // expecting violations to be a JSON object.
-      JSONObject violations = new JSONObject();
-      if (ex instanceof NumberFormatException) {
- violations.put("Expected a number instead of String", ex.getMessage());
-      } else {
-        violations.put("", "unexpected server error");
-      }
-      returnObject.put("violations", violations);
-    } catch (JSONException e) {
-      // ignore.
-      e.printStackTrace();
-    }
-    return returnObject;
-  }

   public JSONObject getViolationsAsJson(
       Set<ConstraintViolation<Object>> violations) throws JSONException {
     JSONObject violationsAsJson = new JSONObject();
     for (ConstraintViolation<Object> violation : violations) {
-      violationsAsJson.put(violation.getPropertyPath().toString(),
+      violationsAsJson.put(violation.getPropertyPath().toString(),
           violation.getMessage());
     }
     return violationsAsJson;
   }

- public Object invokeStaticDomainMethod(Method domainMethod, Object args[]) + public Object invokeDomainMethod(Object domainObject, Method domainMethod, Object args[])
       throws IllegalAccessException, InvocationTargetException {
-    return domainMethod.invoke(null, args);
+    return domainMethod.invoke(domainObject, args);
   }

   public Object processJsonRequest(String jsonRequestString)
@@ -549,68 +637,53 @@
     RequestDefinition operation;
     JSONObject topLevelJsonObject = new JSONObject(jsonRequestString);

-    String operationName = topLevelJsonObject.getString(
-        RequestData.OPERATION_TOKEN);
-
-    String propertyRefsString =
-        topLevelJsonObject.has(RequestData.PROPERTY_REF_TOKEN) ?
- topLevelJsonObject.getString(RequestData.PROPERTY_REF_TOKEN) : ""; + String operationName = topLevelJsonObject.getString(RequestData.OPERATION_TOKEN); + String propertyRefsString = topLevelJsonObject.has(RequestData.PROPERTY_REF_TOKEN) + ? topLevelJsonObject.getString(RequestData.PROPERTY_REF_TOKEN) : "";
     propertyRefs = RequestProperty.parse(propertyRefsString);
-    if (operationName.equals(RequestFactory.SYNC)) {
-      return sync(topLevelJsonObject.getString(RequestData.CONTENT_TOKEN));
-    } else {
-      operation = getOperation(operationName);
-      Class<?> domainClass = Class.forName(operation.getDomainClassName());
-      Method domainMethod = domainClass.getMethod(
-          operation.getDomainMethodName(), operation.getParameterTypes());
-      if (!Modifier.isStatic(domainMethod.getModifiers())) {
-        throw new IllegalArgumentException(
-            "the " + domainMethod.getName() + " is not static");
-      }
-      Object args[] = getObjectsFromParameterMap(
-          getParameterMap(topLevelJsonObject),
-          domainMethod.getParameterTypes());
-      Object result = invokeStaticDomainMethod(domainMethod, args);
-      if ((result instanceof List<?>) != operation.isReturnTypeList()) {
-        throw new IllegalArgumentException(
- String.format("Type mismatch, expected %s%s, but %s returns %s",
-                operation.isReturnTypeList() ? "list of " : "",
-                operation.getReturnType(), domainMethod,
-                domainMethod.getReturnType()));
-      }
-
-      if (result instanceof List<?>) {
-        JSONObject envelop = new JSONObject();
-        envelop.put("result", toJsonArray(operation, result));
-        envelop.put("related", encodeRelatedObjectsToJson());
-        return envelop;
-      } else if (result instanceof Number && !(result instanceof BigDecimal
-          || result instanceof BigInteger)) {
-        return result;
-      } else {
-        JSONObject envelop = new JSONObject();
-        JSONObject jsonObject = toJsonObject(operation, result);
-        envelop.put("result", jsonObject);
-        envelop.put("related", encodeRelatedObjectsToJson());
-        return envelop;
-      }
-    }
-  }
-
-  /**
- * returns true if the property has been requested. TODO: use the properties
-   * that should be coming with the request.
-   *
-   * @param p               the field of entity ref
- * @param propertyContext the root of the current dotted property reference
-   * @return has the property value been requested
-   */
-  public boolean requestedProperty(Property<?> p,
-      RequestProperty propertyContext) {
-    if (Record.class.isAssignableFrom(p.getType())) {
-      return propertyContext.hasProperty(p.getName());
+
+    operation = getOperation(operationName);
+    Class<?> domainClass = Class.forName(operation.getDomainClassName());
+    Method domainMethod = domainClass.getMethod(
+        operation.getDomainMethodName(), operation.getParameterTypes());
+ if (Modifier.isStatic(domainMethod.getModifiers()) == operation.isInstance()) {
+      throw new IllegalArgumentException("the " + domainMethod.getName()
+ + " should " + (operation.isInstance() ? "not " : "") + "be static");
+    }
+
+    entityDataMap = topLevelJsonObject.has(RequestData.CONTENT_TOKEN)
+ ? decodeDVS(topLevelJsonObject.getString(RequestData.CONTENT_TOKEN))
+        : new HashMap<EntityKey, EntityData>();
+
+   // 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.
+
+    if ((result instanceof List<?>) != operation.isReturnTypeList()) {
+      throw new IllegalArgumentException(
+          String.format("Type mismatch, expected %s%s, but %s returns %s",
+              operation.isReturnTypeList() ? "list of " : "",
+              operation.getReturnType(), domainMethod,
+              domainMethod.getReturnType()));
+    }
+
+    if (result instanceof List<?>) {
+      JSONObject envelop = new JSONObject();
+      envelop.put("result", toJsonArray(operation, result));
+      envelop.put("related", encodeRelatedObjectsToJson());
+      return envelop;
+    } else if (result instanceof Number
+ && !(result instanceof BigDecimal || result instanceof BigInteger)) {
+      return result;
     } else {
-      return !BLACK_LIST.contains(p.getName());
+      JSONObject envelop = new JSONObject();
+      JSONObject jsonObject = toJsonObject(operation, result);
+      envelop.put("result", jsonObject);
+      envelop.put("related", encodeRelatedObjectsToJson());
+      return envelop;
     }
   }

@@ -618,24 +691,47 @@
     this.operationRegistry = operationRegistry;
   }

-  public JSONObject sync(String content) throws SecurityException {
-
+  public void validateKeys(JSONObject recordObject,
+      Set<String> declaredProperties) {
+    Iterator<?> keys = recordObject.keys();
+    while (keys.hasNext()) {
+      String key = (String) keys.next();
+      if (!declaredProperties.contains(key)) {
+        throw new IllegalArgumentException("key " + key
+            + " is not permitted to be set");
+      }
+    }
+  }
+
+  private void addRelatedObject(String keyRef, Object returnValue,
+ Class<? extends Record> propertyType, RequestProperty propertyContext)
+      throws JSONException, IllegalAccessException, NoSuchMethodException,
+      InvocationTargetException {
+ Class<? extends Record> clazz = (Class<? extends Record>) returnValue.getClass();
+
+    relatedObjects.put(keyRef, getJsonObject(returnValue, propertyType,
+        propertyContext));
+  }
+
+  /**
+   * Decode deltaValueStore.
+   */
+  private Map<EntityKey, EntityData> decodeDVS(String content)
+      throws SecurityException {
+
+ Map<EntityKey, EntityData> returnMap = new HashMap<EntityKey, EntityData>();
     try {
       JSONObject jsonObject = new JSONObject(content);
-      JSONObject returnJsonObject = new JSONObject();
       for (WriteOperation writeOperation : WriteOperation.values()) {
         if (!jsonObject.has(writeOperation.name())) {
           continue;
         }
         JSONArray reportArray = new JSONArray(
             jsonObject.getString(writeOperation.name()));
-        JSONArray returnArray = new JSONArray();
-
         int length = reportArray.length();
         if (length == 0) {
-          throw new IllegalArgumentException(
-              "No json array for " + writeOperation.name()
-                  + " should have been sent");
+          throw new IllegalArgumentException("No json array for "
+              + writeOperation.name() + " should have been sent");
         }
         for (int i = 0; i < length; i++) {
           JSONObject recordWithSchema = reportArray.getJSONObject(i);
@@ -646,143 +742,75 @@
                 "There cannot be more than one record token");
           }
JSONObject recordObject = recordWithSchema.getJSONObject(recordToken);
-          JSONObject returnObject = updateRecordInDataStore(recordToken,
+          EntityData entityData = getEntityDataForRecord(recordToken,
               recordObject, writeOperation);
-          returnArray.put(returnObject);
-        }
-        returnJsonObject.put(writeOperation.name(), returnArray);
-      }
-      return returnJsonObject;
+          returnMap.put(entityData.entityKey, entityData);
+        }
+      }
+      return returnMap;
     } catch (JSONException e) {
       throw new IllegalArgumentException("sync failed: ", e);
     }
   }

-  /**
-   * Update propertiesInRecord based on the types of entity.
-   */
-  public void updatePropertyTypes(Map<String, Class<?>> propertiesInRecord,
-      Class<?> entity) {
-    for (Field field : entity.getDeclaredFields()) {
-      Class<?> fieldType = propertiesInRecord.get(field.getName());
-      if (fieldType != null) {
-        propertiesInRecord.put(field.getName(), field.getType());
-      }
-    }
+  private JSONObject encodeRelatedObjectsToJson() throws JSONException {
+    JSONObject array = new JSONObject();
+    for (Map.Entry<String, JSONObject> entry : relatedObjects.entrySet()) {
+      array.put(entry.getKey(), entry.getValue());
+    }
+    return array;
   }

-  /**
- * Persist a recordObject of token "recordToken" and return useful information
-   * as a JSONObject to return back. <p> Example: recordToken = "Employee",
-   * entity = Employee.class, record = EmployeeRecord.class <p> Steps: <ol>
- * <li>assert that each property is present in "EmployeeRecord" <li>invoke
-   * "findEmployee (id)" OR new Employee() <li>set various fields on the
-   * attached entity and persist OR remove() <li>return data </ol>
-   */
-  public JSONObject updateRecordInDataStore(String recordToken,
-      JSONObject recordObject, WriteOperation writeOperation) {
-
+  private EntityData getEntityDataForException(EntityKey entityKey,
+      WriteOperation writeOperation, Exception ex) {
+
+    JSONObject violations = null;
     try {
- Class<? extends Record> record = getRecordFromClassToken(recordToken);
-      Class<?> entity = getEntityFromRecordAnnotation(record);
-
-      Map<String, Class<?>> propertiesInRecord = getPropertiesFromRecord(
-          record);
-      validateKeys(recordObject, propertiesInRecord.keySet());
-      updatePropertyTypes(propertiesInRecord, entity);
-
-      // get entityInstance
-      Object entityInstance = getEntityInstance(writeOperation, entity,
-          recordObject.get("id"), propertiesInRecord.get("id"));
-
-      // persist
-
-      Set<ConstraintViolation<Object>> violations = Collections.emptySet();
-
-      if (writeOperation == WriteOperation.DELETE) {
-        entity.getMethod("remove").invoke(entityInstance);
+      // expecting violations to be a JSON object.
+      violations = new JSONObject();
+      if (ex instanceof NumberFormatException) {
+ violations.put("Expected a number instead of String", ex.getMessage());
       } else {
-        Iterator<?> keys = recordObject.keys();
-        while (keys.hasNext()) {
-          String key = (String) keys.next();
-          Class<?> propertyType = propertiesInRecord.get(key);
- if (writeOperation == WriteOperation.CREATE && ("id".equals(key))) {
-            Long id = generateIdForCreate(key);
-            if (id != null) {
-              entity.getMethod(getMethodNameFromPropertyName(key, "set"),
-                  propertyType).invoke(entityInstance, id);
-            }
-          } else {
- Object propertyValue = getPropertyValueFromRequest(recordObject,
-                key, propertyType);
-            entity.getMethod(getMethodNameFromPropertyName(key, "set"),
-                propertyType).invoke(entityInstance, propertyValue);
-          }
-        }
-
-        // validations check..
-        Validator validator = null;
-        try {
- ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
-          validator = validatorFactory.getValidator();
-        } catch (Exception e) {
-          /*
- * This is JBoss's clumsy way of telling us that the system has not
-           * been configured.
-           */
-          log.info(String.format(
- "Ingnoring exception caught initializing bean validation framework. " - + "It is probably unconfigured or misconfigured. [%s] %s ",
-              e.getClass().getName(), e.getLocalizedMessage()));
-        }
-
-        if (validator != null) {
-          violations = validator.validate(entityInstance);
-        }
-        if (violations.isEmpty()) {
-          entity.getMethod("persist").invoke(entityInstance);
-        }
-      }
-
-      // return data back.
-      return getReturnRecord(writeOperation, entityInstance, recordObject,
-          violations, record);
-    } catch (Exception ex) {
-      log.severe(String.format("Caught exception [%s] %s",
-          ex.getClass().getName(), ex.getLocalizedMessage()));
-      return getReturnRecordForException(writeOperation, recordObject, ex);
-    }
+        violations.put("", "unexpected server error");
+      }
+    } catch (JSONException e) {
+      // ignore.
+      e.printStackTrace();
+    }
+ return new EntityData(entityKey, null, violations, writeOperation, null);
   }

-  public void validateKeys(JSONObject recordObject,
-      Set<String> declaredProperties) {
-    Iterator<?> keys = recordObject.keys();
-    while (keys.hasNext()) {
-      String key = (String) keys.next();
-      if (!declaredProperties.contains(key)) {
-        throw new IllegalArgumentException(
-            "key " + key + " is not permitted to be set");
-      }
-    }
-  }
-
-  private void addRelatedObject(String keyRef, Object returnValue,
- Class<? extends Record> propertyType, RequestProperty propertyContext)
-      throws JSONException, IllegalAccessException, NoSuchMethodException,
-      InvocationTargetException {
-    Class<? extends Record> clazz =
-        (Class<? extends Record>) returnValue.getClass();
-
-    relatedObjects.put(keyRef, getJsonObject(returnValue, propertyType,
-        propertyContext));
+  /**
+   * Given param0, decodeId and FutureId. String is of the form "239-NO" or
+   * "239-IS". the latter is a futureId
+   */
+  private Object getEntityInstance(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]),
+        getRecordFromClassToken(parts[2]));
+    EntityData entityData = entityDataMap.get(entityKey);
+    assert entityData != null;
+    return entityData.entityInstance;
   }

-  private JSONObject encodeRelatedObjectsToJson() throws JSONException {
-    JSONObject array = new JSONObject();
-    for (Map.Entry<String, JSONObject> entry : relatedObjects.entrySet()) {
-      array.put(entry.getKey(), entry.getValue());
-    }
-    return array;
+  /**
+ * returns true if the property has been requested. TODO: use the properties
+   * that should be coming with the request.
+   *
+   * @param p the field of entity ref
+ * @param propertyContext the root of the current dotted property reference
+   * @return has the property value been requested
+   */
+  private boolean requestedProperty(Property<?> p,
+      RequestProperty propertyContext) {
+    if (Record.class.isAssignableFrom(p.getType())) {
+      return propertyContext.hasProperty(p.getName());
+    } else {
+      return !BLACK_LIST.contains(p.getName());
+    }
   }

   @SuppressWarnings("unchecked")
@@ -802,4 +830,17 @@
         (Class<? extends Record>) operation.getReturnType(), propertyRefs);
     return jsonObject;
   }
-}
+
+  /**
+   * Update propertiesInRecord based on the types of entity.
+   */
+ private void updatePropertyTypes(Map<String, Class<?>> propertiesInRecord,
+      Class<?> entity) {
+    for (Field field : entity.getDeclaredFields()) {
+      Class<?> fieldType = propertiesInRecord.get(field.getName());
+      if (fieldType != null) {
+        propertiesInRecord.put(field.getName(), field.getType());
+      }
+    }
+  }
+}
=======================================
--- /trunk/user/src/com/google/gwt/requestfactory/server/ReflectionBasedOperationRegistry.java Fri Aug 13 14:38:39 2010 +++ /trunk/user/src/com/google/gwt/requestfactory/server/ReflectionBasedOperationRegistry.java Tue Aug 17 09:57:01 2010
@@ -16,6 +16,7 @@
 package com.google.gwt.requestfactory.server;

 import com.google.gwt.requestfactory.shared.DataTransferObject;
+import com.google.gwt.requestfactory.shared.Instance;
 import com.google.gwt.requestfactory.shared.RequestObject;
 import com.google.gwt.requestfactory.shared.Service;
 import com.google.gwt.valuestore.shared.Record;
@@ -49,12 +50,15 @@

     private Method domainMethod;

+    private boolean isInstance;
+
     public ReflectiveRequestDefinition(Class<?> requestClass,
-        Method requestMethod, Class<?> domainClass, Method domainMethod) {
+ Method requestMethod, Class<?> domainClass, Method domainMethod, boolean isInstance) {
       this.requestClass = requestClass;
       this.requestMethod = requestMethod;
       this.domainClass = domainClass;
       this.domainMethod = domainMethod;
+      this.isInstance = isInstance;
     }

     public String getDomainClassName() {
@@ -95,6 +99,10 @@
       // primitive ?
       return requestReturnType;
     }
+
+    public boolean isInstance() {
+      return isInstance;
+    }

     public boolean isReturnTypeList() {
       return List.class.isAssignableFrom(domainMethod.getReturnType());
@@ -171,9 +179,15 @@
         Class<?> domainClass = domainClassAnnotation.value();
         Method requestMethod = findMethod(requestClass, domainMethodName);
         Method domainMethod = findMethod(domainClass, domainMethodName);
+ boolean isInstance = (requestMethod.getAnnotation(Instance.class) != null);
         if (requestMethod != null && domainMethod != null) {
+ if (isInstance == Modifier.isStatic(domainMethod.getModifiers())) { + throw new IllegalArgumentException("domain method " + domainMethod
+                + " and interface method " + requestMethod
+                + " don't match wrt instance/static");
+          }
return new ReflectiveRequestDefinition(requestClass, requestMethod,
-              domainClass, domainMethod);
+              domainClass, domainMethod, isInstance);
         }
       }
       return null;
=======================================
--- /trunk/user/src/com/google/gwt/requestfactory/server/RequestDefinition.java Fri Jul 30 11:32:15 2010 +++ /trunk/user/src/com/google/gwt/requestfactory/server/RequestDefinition.java Tue Aug 17 09:57:01 2010
@@ -46,6 +46,11 @@
    */
   Class<?> getReturnType();

+  /**
+   * Returns true if the domain method is an instance method.
+   */
+  boolean isInstance();
+
   /**
    * Returns true if the request returns Lists of {...@link #getReturnType},
    * false for single instances.
=======================================
--- /trunk/user/src/com/google/gwt/requestfactory/server/SampleDataPopulator.java Thu Aug 5 15:15:34 2010 +++ /trunk/user/src/com/google/gwt/requestfactory/server/SampleDataPopulator.java Tue Aug 17 09:57:01 2010
@@ -88,7 +88,8 @@
       IOException, JSONException {
     PostMethod post = new PostMethod(url);
     JSONObject request = new JSONObject();
-    request.put(RequestData.OPERATION_TOKEN, RequestFactory.SYNC);
+    // TODO: fix
+    request.put(RequestData.OPERATION_TOKEN, "DOESNT_WORK");
     request.put(RequestData.CONTENT_TOKEN, contentData);
     post.setRequestBody(request.toString());
     HttpClient client = new HttpClient();
=======================================
--- /trunk/user/src/com/google/gwt/requestfactory/shared/RequestData.java Fri Aug 13 14:38:39 2010 +++ /trunk/user/src/com/google/gwt/requestfactory/shared/RequestData.java Tue Aug 17 09:57:01 2010
@@ -57,10 +57,6 @@
    */
   public Map<String, String> getRequestMap(String contentData) {
     Map<String, String> requestMap = new HashMap<String, String>();
-    // nasty hack, remove.
-    if (operation.endsWith("persist") || operation.endsWith("remove")) {
-      operation = RequestFactory.SYNC;
-    }
     requestMap.put(OPERATION_TOKEN, operation);
     if (parameters != null) {
       for (int i = 0; i < parameters.length; i++) {
=======================================
--- /trunk/user/src/com/google/gwt/requestfactory/shared/RequestFactory.java Mon Aug 16 20:22:31 2010 +++ /trunk/user/src/com/google/gwt/requestfactory/shared/RequestFactory.java Tue Aug 17 09:57:01 2010
@@ -27,9 +27,7 @@
  * Marker interface for the RequestFactory code generator.
  */
 public interface RequestFactory {
- static final String JSON_CONTENT_TYPE_UTF8 = "application/json; charset=utf-8";
-
-  String SYNC = "SYNC";
+  String JSON_CONTENT_TYPE_UTF8 = "application/json; charset=utf-8";

   // TODO: this must be configurable
   String URL = "gwtRequest";
=======================================
--- /trunk/user/test/com/google/gwt/requestfactory/server/JsonRequestProcessorTest.java Fri Aug 13 14:38:39 2010 +++ /trunk/user/test/com/google/gwt/requestfactory/server/JsonRequestProcessorTest.java Tue Aug 17 09:57:01 2010
@@ -16,8 +16,6 @@
 package com.google.gwt.requestfactory.server;

 import com.google.gwt.requestfactory.shared.RequestData;
-import com.google.gwt.requestfactory.shared.RequestFactory;
-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;
@@ -124,24 +122,30 @@
       JSONArray arr = new JSONArray();
       arr.put(recordWithSchema);
       JSONObject operation = new JSONObject();
+
       operation.put(WriteOperation.UPDATE.toString(), arr);
       JSONObject sync = new JSONObject();
-      sync.put(RequestData.OPERATION_TOKEN, RequestFactory.SYNC);
+ sync.put(RequestData.OPERATION_TOKEN, "com.google.gwt.valuestore.shared.SimpleFooRequest::persist");
       sync.put(RequestData.CONTENT_TOKEN, operation.toString());
+ sync.put(RequestData.PARAM_TOKEN + "0", foo.getInt("id") + "-NO" + "-"
+          + SimpleFooRecord.class.getName());
JSONObject result = (JSONObject) requestProcessor.processJsonRequest(sync.toString());

+      // 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.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();
       fail(e.toString());
     }
   }
=======================================
--- /trunk/user/test/com/google/gwt/valuestore/shared/SimpleFooRequest.java Fri Jul 30 17:29:09 2010 +++ /trunk/user/test/com/google/gwt/valuestore/shared/SimpleFooRequest.java Tue Aug 17 09:57:01 2010
@@ -15,18 +15,25 @@
  */
 package com.google.gwt.valuestore.shared;

+import com.google.gwt.requestfactory.shared.Instance;
 import com.google.gwt.requestfactory.shared.RecordListRequest;
 import com.google.gwt.requestfactory.shared.RecordRequest;
 import com.google.gwt.requestfactory.shared.RequestObject;
 import com.google.gwt.requestfactory.shared.Service;

 /**
-   * Do nothing test interface.
-   */
-  @Service(com.google.gwt.valuestore.server.SimpleFoo.class)
-  public interface SimpleFooRequest {
-    RequestObject<Long> countSimpleFoo();
-    RecordListRequest<SimpleFooRecord> findAll();
-    RecordRequest<SimpleFooRecord> findSimpleFooById(Long id);
-    RequestObject<Integer> privateMethod();
-  }
+ * Do nothing test interface.
+ */
+...@service(com.google.gwt.valuestore.server.SimpleFoo.class)
+public interface SimpleFooRequest {
+  RequestObject<Long> countSimpleFoo();
+
+  RecordListRequest<SimpleFooRecord> findAll();
+
+  RecordRequest<SimpleFooRecord> findSimpleFooById(Long id);
+
+  RequestObject<Integer> privateMethod();
+
+  @Instance
+  RequestObject<Void> persist(SimpleFooRecord record);
+}

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

Reply via email to