Add graceful failure when a client sends a deleted EntityProxy in a request.
Resolves GWT issue 5403.
Patch by: bobv
Review by: rjrjr
Review at http://gwt-code-reviews.appspot.com/986802
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9034 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/requestfactory/server/DeadEntityException.java b/user/src/com/google/gwt/requestfactory/server/DeadEntityException.java
new file mode 100644
index 0000000..008fe4c
--- /dev/null
+++ b/user/src/com/google/gwt/requestfactory/server/DeadEntityException.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+/**
+ * Indicates the user attempted to perform an operation on an irretrievable
+ * entity
+ */
+public class DeadEntityException extends RuntimeException {
+ public DeadEntityException() {
+ }
+
+ public DeadEntityException(String message) {
+ super(message);
+ }
+}
diff --git a/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java b/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java
index d422b5c..ce0f692 100644
--- a/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java
+++ b/user/src/com/google/gwt/requestfactory/server/JsonRequestProcessor.java
@@ -237,6 +237,9 @@
JSONObject exceptionResponse = buildExceptionResponse(e.getCause());
throw new RequestProcessingException("Unexpected exception", e,
exceptionResponse.toString());
+ } catch (DeadEntityException e) {
+ // This is a normal, if exceptional, condition
+ return buildExceptionResponse(e).toString();
} catch (Exception e) {
JSONObject exceptionResponse = buildExceptionResponse(e);
throw new RequestProcessingException("Unexpected exception", e,
@@ -466,6 +469,10 @@
Class<?> idType = getIdMethodForEntity(entityType).getReturnType();
Object entityInstance = getEntityInstance(writeOperation, entityType,
entityKey.decodedId(idType), idType);
+ if (entityInstance == null) {
+ throw new DeadEntityException(
+ "The requested entity is not available on the server");
+ }
cachedEntityLookup.put(entityKey, entityInstance);
Iterator<?> keys = recordObject.keys();
diff --git a/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java b/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java
index 158331b..048745d 100644
--- a/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java
+++ b/user/test/com/google/gwt/requestfactory/client/RequestFactoryTest.java
@@ -924,7 +924,7 @@
*/
public void testNullValueInIntegerListRequest() {
delayTestFinish(DELAY_TEST_FINISH);
- List<Integer> list = Arrays.asList(new Integer[]{1, 2, null});
+ List<Integer> list = Arrays.asList(new Integer[] {1, 2, null});
final Request<Void> fooReq = req.simpleFooRequest().receiveNullValueInIntegerList(
list);
fooReq.fire(new Receiver<Void>() {
@@ -940,7 +940,7 @@
*/
public void testNullValueInStringListRequest() {
delayTestFinish(DELAY_TEST_FINISH);
- List<String> list = Arrays.asList(new String[]{"nonnull", "null", null});
+ List<String> list = Arrays.asList(new String[] {"nonnull", "null", null});
final Request<Void> fooReq = req.simpleFooRequest().receiveNullValueInStringList(
list);
fooReq.fire(new Receiver<Void>() {
@@ -1762,7 +1762,7 @@
@Override
public void onSuccess(SimpleFooProxy response) {
assertEquals(2, response.getOneToManyField().size());
-
+
// Check lists of proxies returned from a mutable object are mutable
response = simpleFooRequest().edit(response);
response.getOneToManyField().get(0).setUserName("canMutate");
@@ -1960,6 +1960,64 @@
});
}
+ /**
+ * This is analagous to FindServiceTest.testFetchDeletedEntity() only we're
+ * trying to invoke a method on the deleted entity using a stale EntityProxy
+ * reference on the client.
+ */
+ public void testUseOfDeletedEntity() {
+ delayTestFinish(DELAY_TEST_FINISH);
+ SimpleBarRequest context = simpleBarRequest();
+ SimpleBarProxy willDelete = context.create(SimpleBarProxy.class);
+ willDelete.setUserName("A");
+
+ // Persist the newly-created object
+ context.persistAndReturnSelf().using(willDelete).fire(
+ new Receiver<SimpleBarProxy>() {
+ @Override
+ public void onSuccess(SimpleBarProxy response) {
+ assertEquals("A", response.getUserName());
+ // Mark the object as deleted
+ SimpleBarRequest context = simpleBarRequest();
+ response = context.edit(response);
+ response.setFindFails(true);
+ response.setUserName("B");
+ context.persistAndReturnSelf().using(response).fire(
+ new Receiver<SimpleBarProxy>() {
+
+ @Override
+ public void onSuccess(SimpleBarProxy response) {
+ // The last-known state should be returned
+ assertNotNull(response);
+ assertEquals("B", response.getUserName());
+
+ SimpleBarRequest context = simpleBarRequest();
+ // Ensure attempts to mutate deleted objects don't blow up
+ response = context.edit(response);
+ response.setUserName("C");
+
+ // Attempting to use the now-deleted object should fail
+ context.persistAndReturnSelf().using(response).fire(
+ new Receiver<SimpleBarProxy>() {
+ @Override
+ public void onFailure(ServerFailure error) {
+ assertTrue(error.getMessage().contains(
+ "The requested entity is not available on"
+ + " the server"));
+ finishTestAndReset();
+ }
+
+ @Override
+ public void onSuccess(SimpleBarProxy response) {
+ fail();
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+
public void testViolationAbsent() {
delayTestFinish(DELAY_TEST_FINISH);