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