Make RequestFactory.getHistoryToken() play nicely with RF.getClass() and RF.getProxyId().
Patch by: bobv
Review by: rjrjr, pdr
Review at http://gwt-code-reviews.appspot.com/911801
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8840 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/FavoritesManager.java b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/FavoritesManager.java
index 0268fea..b291835 100644
--- a/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/FavoritesManager.java
+++ b/samples/dynatablerf/src/com/google/gwt/sample/dynatablerf/client/FavoritesManager.java
@@ -47,9 +47,10 @@
if (fragment.length() == 0) {
continue;
}
- @SuppressWarnings("unchecked")
- EntityProxyId<PersonProxy> id = (EntityProxyId<PersonProxy>) requestFactory.getProxyId(fragment);
- favoriteIds.add(id);
+ EntityProxyId<PersonProxy> id = requestFactory.getProxyId(fragment);
+ if (id != null) {
+ favoriteIds.add(id);
+ }
}
} catch (NumberFormatException e) {
// Not a big deal, start up without favorites
diff --git a/user/src/com/google/gwt/requestfactory/client/impl/EntityProxyIdImpl.java b/user/src/com/google/gwt/requestfactory/client/impl/EntityProxyIdImpl.java
index fe27403..00f185e 100644
--- a/user/src/com/google/gwt/requestfactory/client/impl/EntityProxyIdImpl.java
+++ b/user/src/com/google/gwt/requestfactory/client/impl/EntityProxyIdImpl.java
@@ -35,7 +35,6 @@
* created on this client.
*/
final class EntityProxyIdImpl<P extends EntityProxy> implements EntityProxyId<P> {
- static final String SEPARATOR = "---";
private static int hashCode(ProxySchema<?> proxySchema, boolean hasFutureId, Object finalId) {
final int prime = 31;
@@ -66,13 +65,6 @@
this.futureId = futureId;
}
- public String asString() {
- if (isFuture) {
- throw new IllegalStateException("Need to persist this proxy first");
- }
- return encodedId + SEPARATOR + schema.getToken();
- }
-
@Override
public boolean equals(Object obj) {
if (this == obj) {
diff --git a/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java b/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java
index 4fa03b3..28f5d51 100644
--- a/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java
+++ b/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java
@@ -67,6 +67,10 @@
static final boolean IS_FUTURE = true;
static final boolean NOT_FUTURE = false;
+ private static final String FUTURE_TOKEN = "F";
+ private static final String HISTORY_TOKEN_SEPARATOR = "--";
+ private static final int ID_INDEX = 0;
+ private static final int SCHEMA_INDEX = 1;
private static Logger logger = Logger.getLogger(RequestFactory.class.getName());
@@ -113,8 +117,7 @@
String payload = ClientRequestHelper.getRequestString(requestMap);
transport.send(payload, new RequestTransport.TransportReceiver() {
public void onTransportFailure(String message) {
- abstractRequest.fail(new ServerFailure(message, null,
- null));
+ abstractRequest.fail(new ServerFailure(message, null, null));
}
public void onTransportSuccess(String payload) {
@@ -155,7 +158,7 @@
public void initialize(EventBus eventBus) {
initialize(eventBus, new DefaultRequestTransport(eventBus));
}
-
+
public void initialize(EventBus eventBus, RequestTransport transport) {
this.valueStore = new ValueStoreJsonImpl();
this.eventBus = eventBus;
@@ -165,47 +168,76 @@
protected abstract FindRequest findRequest();
+ /**
+ * This implementation cannot be changed without breaking clients.
+ */
protected Class<? extends EntityProxy> getClass(String token,
ProxyToTypeMap recordToTypeMap) {
- String[] bits = token.split("-");
- ProxySchema<? extends EntityProxy> schema = recordToTypeMap.getType(bits[0]);
+ String[] bits = token.split(HISTORY_TOKEN_SEPARATOR);
+ String schemaToken;
+ if (bits.length == 1) {
+ schemaToken = token;
+ } else if (bits.length == 2 || bits.length == 3) {
+ schemaToken = bits[SCHEMA_INDEX];
+ } else {
+ return null;
+ }
+ ProxySchema<? extends EntityProxy> schema = recordToTypeMap.getType(schemaToken);
if (schema == null) {
return null;
}
return schema.getProxyClass();
}
- protected String getHistoryToken(EntityProxyId proxyId, ProxyToTypeMap recordToTypeMap) {
- EntityProxyIdImpl entityProxyId = (EntityProxyIdImpl) proxyId;
- Class<? extends EntityProxy> proxyClass = entityProxyId.schema.getProxyClass();
- String rtn = recordToTypeMap.getClassToken(proxyClass) + "-";
- Object datastoreId = entityProxyId.encodedId;
+ /**
+ * This implementation cannot be changed without breaking clients.
+ */
+ protected String getHistoryToken(EntityProxyId<?> proxyId,
+ ProxyToTypeMap recordToTypeMap) {
+ EntityProxyIdImpl<?> entityProxyId = (EntityProxyIdImpl<?>) proxyId;
+ StringBuilder toReturn = new StringBuilder();
+ boolean isFuture = false;
+ Object tokenId = entityProxyId.encodedId;
if (entityProxyId.isFuture) {
- datastoreId = futureToDatastoreMap.get(entityProxyId.encodedId);
+ // See if the associated entityproxy has been persisted in the meantime
+ Object persistedId = futureToDatastoreMap.get(entityProxyId.encodedId);
+ if (persistedId == null) {
+ // Return a future token
+ isFuture = true;
+ } else {
+ // Use the persisted id instead
+ tokenId = persistedId;
+ }
}
- if (datastoreId == null) {
- rtn += "0-FUTURE";
- } else {
- rtn += datastoreId;
+ toReturn = new StringBuilder();
+ toReturn.append(tokenId);
+ toReturn.append(HISTORY_TOKEN_SEPARATOR).append(
+ entityProxyId.schema.getToken());
+ if (isFuture) {
+ toReturn.append(HISTORY_TOKEN_SEPARATOR).append(FUTURE_TOKEN);
}
- return rtn;
+ return toReturn.toString();
}
- protected EntityProxyId getProxyId(String token,
+ /**
+ * This implementation cannot be changed without breaking clients.
+ */
+ protected EntityProxyId<?> getProxyId(String historyToken,
ProxyToTypeMap recordToTypeMap) {
- String[] bits = token.split(EntityProxyIdImpl.SEPARATOR);
- if (bits.length != 2) {
+ String[] bits = historyToken.split(HISTORY_TOKEN_SEPARATOR);
+ if (bits.length < 2 || bits.length > 3) {
return null;
}
+ boolean isFuture = bits.length == 3;
- ProxySchema<? extends EntityProxy> schema = recordToTypeMap.getType(bits[1]);
+ ProxySchema<? extends EntityProxy> schema = recordToTypeMap.getType(bits[SCHEMA_INDEX]);
if (schema == null) {
return null;
}
- String id = bits[0];
+ String id = bits[ID_INDEX];
Object futureId = datastoreToFutureMap.get(id, schema);
- return new EntityProxyIdImpl(id, schema, false, futureId);
+ return new EntityProxyIdImpl<EntityProxy>(id, schema, isFuture, futureId);
}
ValueStoreJsonImpl getValueStore() {
@@ -230,8 +262,8 @@
private <R extends ProxyImpl> R createFuture(ProxySchema<R> schema) {
Long futureId = ++currentFutureId;
- ProxyJsoImpl newRecord = ProxyJsoImpl.create(Long.toString(futureId), initialVersion,
- schema, this);
+ ProxyJsoImpl newRecord = ProxyJsoImpl.create(Long.toString(futureId),
+ initialVersion, schema, this);
return schema.create(newRecord, IS_FUTURE);
}
}
diff --git a/user/src/com/google/gwt/requestfactory/shared/RequestFactory.java b/user/src/com/google/gwt/requestfactory/shared/RequestFactory.java
index 0c4a04c..882645b 100644
--- a/user/src/com/google/gwt/requestfactory/shared/RequestFactory.java
+++ b/user/src/com/google/gwt/requestfactory/shared/RequestFactory.java
@@ -1,12 +1,12 @@
/*
* 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
@@ -43,39 +43,49 @@
*/
<P extends EntityProxy> Class<P> getClass(EntityProxyId<P> proxyId);
- /**
+/**
* Return the class object which may be used to create new instances of the
* type of this token, via {@link #create}. The token may represent either a
- * proxy instance (see {@link #getHistoryToken(Proxy)) or a proxy class (see
- * @link #getToken(Class)}).
+ * proxy instance (see {@link #getHistoryToken(Proxy)}) or a proxy class (see
+ * {@link #getToken(Class)}).
*/
Class<? extends EntityProxy> getClass(String token);
/**
- * @return the eventbus this factory's events are posted on, which was set via
- * {@link #initialize}
+ * Returns the eventbus this factory's events are posted on, which was set via
+ * {@link #initialize}.
*/
EventBus getEventBus();
/**
* Get a {@link com.google.gwt.user.client.History} compatible token that
- * represents the given proxy. It can be processed by
+ * represents the given proxy id. It can be processed by
* {@link #getProxyId(String)} and {@link #getClass(String)}.
- *
+ * <p>
+ * The history token returned for an EntityProxyId associated with a
+ * newly-created (future) EntityProxy will differ from the token returned by this
+ * method after the EntityProxy has been persisted. Once an EntityProxy has
+ * been persisted, the return value for this method will always be stable,
+ * regardless of when the EntityProxyId was retrieved relative to the persist
+ * operation. In other words, the "future" history token returned for an
+ * as-yet-unpersisted EntityProxy is only valid for the duration of the
+ * RequestFactory's lifespan.
+ *
* @return a {@link com.google.gwt.user.client.History} compatible token
*/
String getHistoryToken(EntityProxyId<?> proxy);
/**
- * Return the appropriate {@link EntityProxyId}, a stable id for the Proxy.
+ * Return the appropriate {@link EntityProxyId} using a string returned from
+ * {@link #getHistoryToken(EntityProxyId)}.
*/
- EntityProxyId<?> getProxyId(String token);
+ <T extends EntityProxy> EntityProxyId<T> getProxyId(String historyToken);
/**
* Get a {@link com.google.gwt.user.client.History} compatible token that
* represents the given class. It can be processed by
* {@link #getClass(String)}
- *
+ *
* @return a {@link com.google.gwt.user.client.History} compatible token
*/
String getToken(Class<? extends EntityProxy> clazz);
diff --git a/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java b/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java
index 1793b46..4fdc030 100644
--- a/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java
+++ b/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java
@@ -17,6 +17,7 @@
import com.google.gwt.requestfactory.client.impl.ProxyImpl;
import com.google.gwt.requestfactory.shared.EntityProxy;
+import com.google.gwt.requestfactory.shared.EntityProxyId;
import com.google.gwt.requestfactory.shared.Receiver;
import com.google.gwt.requestfactory.shared.Request;
import com.google.gwt.requestfactory.shared.ServerFailure;
@@ -124,6 +125,14 @@
return "com.google.gwt.requestfactory.RequestFactorySuite";
}
+ public void testClassToken() {
+ String token = req.getToken(SimpleFooProxy.class);
+ assertEquals(SimpleFooProxy.class, req.getClass(token));
+
+ SimpleFooProxy foo = req.create(SimpleFooProxy.class);
+ assertEquals(SimpleFooProxy.class, req.getClass(foo.stableId()));
+ }
+
public void testDummyCreate() {
delayTestFinish(5000);
@@ -170,6 +179,44 @@
});
}
+ public void testHistoryToken() {
+ delayTestFinish(5000);
+ final SimpleBarProxy foo = req.create(SimpleBarProxy.class);
+ final EntityProxyId<SimpleBarProxy> futureId = foo.stableId();
+ final String futureToken = req.getHistoryToken(futureId);
+
+ // Check that a newly-created object's token can be found
+ assertEquals(futureId, req.getProxyId(futureToken));
+ assertEquals(req.getClass(futureId), req.getClass(futureToken));
+
+ RequestObject<SimpleBarProxy> fooReq = req.simpleBarRequest().persistAndReturnSelf(
+ foo);
+ fooReq.fire(new Receiver<SimpleBarProxy>() {
+ @Override
+ public void onSuccess(final SimpleBarProxy returned) {
+ EntityProxyId<SimpleBarProxy> persistedId = returned.stableId();
+ String persistedToken = req.getHistoryToken(returned.stableId());
+
+ // Expect variations after persist
+ assertFalse(futureToken.equals(persistedToken));
+
+ // Make sure the token is stable after persist using the future id
+ assertEquals(persistedToken, req.getHistoryToken(futureId));
+
+ // Check that the persisted object can be found with future token
+ assertEquals(futureId, req.getProxyId(futureToken));
+ assertEquals(futureId, req.getProxyId(persistedToken));
+ assertEquals(req.getClass(futureId), req.getClass(persistedToken));
+
+ assertEquals(persistedId, req.getProxyId(futureToken));
+ assertEquals(persistedId, req.getProxyId(persistedToken));
+ assertEquals(req.getClass(persistedId), req.getClass(futureToken));
+
+ finishTestAndReset();
+ }
+ });
+ }
+
public void testFetchEntity() {
delayTestFinish(5000);
req.simpleFooRequest().findSimpleFooById(999L).fire(