Revision: 7069
Author: b...@google.com
Date: Fri Nov 20 10:37:09 2009
Log: Merge tr...@7049,7065 to better handle RuntimeExceptions throw by  
deRPC service methods.
$ svn merge --ignore-ancestry -c7049,7065  
https://google-web-toolkit.googlecode.com/svn/trunk .


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

Modified:
  /releases/2.0/branch-info.txt
  /releases/2.0/user/src/com/google/gwt/rpc/server/RPC.java
   
/releases/2.0/user/src/com/google/gwt/user/server/rpc/AbstractRemoteServiceServlet.java
  /releases/2.0/user/src/com/google/gwt/user/server/rpc/RPC.java
  /releases/2.0/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java
   
/releases/2.0/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTest.java
   
/releases/2.0/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTestService.java
   
/releases/2.0/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTestServiceAsync.java
   
/releases/2.0/user/test/com/google/gwt/user/server/rpc/RemoteServiceServletTestServiceImplBase.java

=======================================
--- /releases/2.0/branch-info.txt       Fri Nov 20 09:51:31 2009
+++ /releases/2.0/branch-info.txt       Fri Nov 20 10:37:09 2009
@@ -870,3 +870,7 @@
    Update IE installer to run without admin privileges
    svn merge --ignore-ancestry -c7066  
https://google-web-toolkit.googlecode.com/svn/trunk .

+tr...@7049,7065 were merged into this branch
+  Ensures correct handling of (unknown) RuntimeExceptions thrown from  
deRPC service methods.
+  svn merge --ignore-ancestry -c7049,7065  
https://google-web-toolkit.googlecode.com/svn/trunk .
+
=======================================
--- /releases/2.0/user/src/com/google/gwt/rpc/server/RPC.java   Mon Nov 16  
10:54:36 2009
+++ /releases/2.0/user/src/com/google/gwt/rpc/server/RPC.java   Fri Nov 20  
10:37:09 2009
@@ -26,6 +26,8 @@
  import com.google.gwt.user.client.rpc.RemoteService;
  import com.google.gwt.user.client.rpc.SerializationException;
  import com.google.gwt.user.server.rpc.RPCRequest;
+import com.google.gwt.user.server.rpc.RPCServletUtils;
+import com.google.gwt.user.server.rpc.UnexpectedException;

  import java.io.IOException;
  import java.io.OutputStream;
@@ -210,8 +212,14 @@
        throw securityException;
      } catch (InvocationTargetException e) {
        // Try to encode the caught exception
-      //
        Throwable cause = e.getCause();
+
+      // Don't allow random RuntimeExceptions to be thrown back to the  
client
+      if (!RPCServletUtils.isExpectedException(serviceMethod, cause)) {
+        throw new UnexpectedException("Service method '"
+            + getSourceRepresentation(serviceMethod)
+            + "' threw an unexpected exception: " + cause.toString(),  
cause);
+      }

        streamResponse(clientOracle, cause, sink, true);
      }
=======================================
---  
/releases/2.0/user/src/com/google/gwt/user/server/rpc/AbstractRemoteServiceServlet.java
  
Wed Nov  4 07:16:01 2009
+++  
/releases/2.0/user/src/com/google/gwt/user/server/rpc/AbstractRemoteServiceServlet.java
  
Fri Nov 20 10:37:09 2009
@@ -94,6 +94,18 @@
     * @param e the exception which was thrown
     */
    protected void doUnexpectedFailure(Throwable e) {
+    try {
+      getThreadLocalResponse().reset();
+    } catch (IllegalStateException ex) {
+      /*
+       * If we can't reset the request, the only way to signal that  
something
+       * has gone wrong is to throw an exception from here. It should be  
the
+       * case that we call the user's implementation code before emitting  
data
+       * into the response, so the only time that gets tripped is if the  
object
+       * serialization code blows up.
+       */
+      throw new RuntimeException("Unable to report failure", e);
+    }
      ServletContext servletContext = getServletContext();
      RPCServletUtils.writeResponseForUnexpectedFailure(servletContext,
          getThreadLocalResponse(), e);
=======================================
--- /releases/2.0/user/src/com/google/gwt/user/server/rpc/RPC.java      Mon Jun 
 
15 14:00:16 2009
+++ /releases/2.0/user/src/com/google/gwt/user/server/rpc/RPC.java      Fri Nov 
 
20 10:37:09 2009
@@ -48,7 +48,9 @@
   * <h3>Advanced Example</h3> The following example shows a more advanced  
way of
   * using this class to create an adapter between GWT RPC entities and  
POJOs.
   *
- * {...@example  
com.google.gwt.examples.rpc.server.AdvancedExample#doPost(javax.servlet.http.HttpServletRequest,
  
javax.servlet.http.HttpServletResponse)}
+ * {...@example
+ *  
com.google.gwt.examples.rpc.server.AdvancedExample#doPost(javax.servlet.http.
+ * HttpServletRequest, javax.servlet.http.HttpServletResponse)}
   */
  public final class RPC {

@@ -270,7 +272,8 @@

        int paramCount = streamReader.readInt();
        if (paramCount > streamReader.getNumberOfTokens()) {
-        throw new IncompatibleRemoteServiceException("Invalid number of  
parameters");
+        throw new IncompatibleRemoteServiceException(
+            "Invalid number of parameters");
        }
        Class<?>[] parameterTypes = new Class[paramCount];

@@ -372,7 +375,8 @@
        throw new NullPointerException("serializationPolicy");
      }

-    if (serviceMethod != null && !RPC.isExpectedException(serviceMethod,  
cause)) {
+    if (serviceMethod != null
+        && !RPCServletUtils.isExpectedException(serviceMethod, cause)) {
        throw new UnexpectedException("Service method '"
            + getSourceRepresentation(serviceMethod)
            + "' threw an unexpected exception: " + cause.toString(), cause);
@@ -774,42 +778,6 @@
          return true;
        }
      }
-
-    return false;
-  }
-
-  /**
-   * Returns true if the {...@link java.lang.reflect.Method Method}  
definition on
-   * the service is specified to throw the exception contained in the
-   * InvocationTargetException or false otherwise. NOTE we do not check  
that the
-   * type is serializable here. We assume that it must be otherwise the
-   * application would never have been allowed to run.
-   *
-   * @param serviceIntfMethod the method from the RPC request
-   * @param cause the exception that the method threw
-   * @return true if the exception's type is in the method's signature
-   */
-  private static boolean isExpectedException(Method serviceIntfMethod,
-      Throwable cause) {
-    assert (serviceIntfMethod != null);
-    assert (cause != null);
-
-    Class<?>[] exceptionsThrown = serviceIntfMethod.getExceptionTypes();
-    if (exceptionsThrown.length <= 0) {
-      // The method is not specified to throw any exceptions
-      //
-      return false;
-    }
-
-    Class<? extends Throwable> causeType = cause.getClass();
-
-    for (Class<?> exceptionThrown : exceptionsThrown) {
-      assert (exceptionThrown != null);
-
-      if (exceptionThrown.isAssignableFrom(causeType)) {
-        return true;
-      }
-    }

      return false;
    }
=======================================
---  
/releases/2.0/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java      
 
Mon Jul  6 16:17:17 2009
+++  
/releases/2.0/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java      
 
Fri Nov 20 10:37:09 2009
@@ -18,6 +18,7 @@
  import java.io.ByteArrayOutputStream;
  import java.io.IOException;
  import java.io.InputStream;
+import java.lang.reflect.Method;
  import java.util.zip.GZIPOutputStream;

  import javax.servlet.ServletContext;
@@ -87,6 +88,42 @@
    public static boolean exceedsUncompressedContentLengthLimit(String  
content) {
      return (content.length() * 2) > UNCOMPRESSED_BYTE_SIZE_LIMIT;
    }
+
+  /**
+   * Returns true if the {...@link java.lang.reflect.Method Method}  
definition on
+   * the service is specified to throw the exception contained in the
+   * InvocationTargetException or false otherwise. NOTE we do not check  
that the
+   * type is serializable here. We assume that it must be otherwise the
+   * application would never have been allowed to run.
+   *
+   * @param serviceIntfMethod the method from the RPC request
+   * @param cause the exception that the method threw
+   * @return true if the exception's type is in the method's signature
+   */
+  public static boolean isExpectedException(Method serviceIntfMethod,
+      Throwable cause) {
+    assert (serviceIntfMethod != null);
+    assert (cause != null);
+
+    Class<?>[] exceptionsThrown = serviceIntfMethod.getExceptionTypes();
+    if (exceptionsThrown.length <= 0) {
+      // The method is not specified to throw any exceptions
+      //
+      return false;
+    }
+
+    Class<? extends Throwable> causeType = cause.getClass();
+
+    for (Class<?> exceptionThrown : exceptionsThrown) {
+      assert (exceptionThrown != null);
+
+      if (exceptionThrown.isAssignableFrom(causeType)) {
+        return true;
+      }
+    }
+
+    return false;
+  }

    /**
     * Returns the content of an {...@link HttpServletRequest} by decoding it  
using
@@ -258,7 +295,12 @@
      try {
        response.setContentType("text/plain");
        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-      response.getWriter().write(GENERIC_FAILURE_MSG);
+      try {
+         
response.getOutputStream().write(GENERIC_FAILURE_MSG.getBytes("UTF-8"));
+      } catch (IllegalStateException e) {
+        // Handle the (unexpected) case where getWriter() was previously  
used
+        response.getWriter().write(GENERIC_FAILURE_MSG);
+      }
      } catch (IOException ex) {
        servletContext.log(
            "respondWithUnexpectedFailure failed while sending the previous  
failure to the client",
=======================================
---  
/releases/2.0/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTest.java
     
Wed Nov  4 13:11:32 2009
+++  
/releases/2.0/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTest.java
     
Fri Nov 20 10:37:09 2009
@@ -206,4 +206,49 @@
      });
      assertTrue(req.isPending());
    }
-}
+
+  /**
+   * Verify behavior when the RPC method throws a RuntimeException  
declared on
+   * the RemoteService interface.
+   */
+  public void testDeclaredRuntimeException() {
+    RemoteServiceServletTestServiceAsync service = getAsyncService();
+
+    delayTestFinishForRpc();
+
+    service.throwDeclaredRuntimeException(new AsyncCallback<Void>() {
+
+      public void onFailure(Throwable caught) {
+        assertTrue(caught instanceof NullPointerException);
+        assertEquals("expected", caught.getMessage());
+        finishTest();
+      }
+
+      public void onSuccess(Void result) {
+        fail();
+      }
+    });
+  }
+
+  /**
+   * Verify behavior when the RPC method throws an unknown RuntimeException
+   * (possibly one unknown to the client).
+   */
+  public void testUnknownRuntimeException() {
+    RemoteServiceServletTestServiceAsync service = getAsyncService();
+
+    delayTestFinishForRpc();
+
+    service.throwUnknownRuntimeException(new AsyncCallback<Void>() {
+
+      public void onFailure(Throwable caught) {
+        assertTrue(caught instanceof InvocationException);
+        finishTest();
+      }
+
+      public void onSuccess(Void result) {
+        fail();
+      }
+    });
+  }
+}
=======================================
---  
/releases/2.0/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTestService.java
      
Thu Mar  5 09:29:25 2009
+++  
/releases/2.0/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTestService.java
      
Fri Nov 20 10:37:09 2009
@@ -25,4 +25,8 @@
    void testExpectCustomHeader();

    void testExpectPermutationStrongName(String expectedStrongName);
-}
+
+  void throwDeclaredRuntimeException() throws NullPointerException;
+
+  void throwUnknownRuntimeException();
+}
=======================================
---  
/releases/2.0/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTestServiceAsync.java
         
Thu Mar  5 09:29:25 2009
+++  
/releases/2.0/user/test/com/google/gwt/user/client/rpc/RemoteServiceServletTestServiceAsync.java
         
Fri Nov 20 10:37:09 2009
@@ -28,4 +28,8 @@

    void testExpectPermutationStrongName(String expectedStrongName,
        AsyncCallback<Void> callback);
-}
+
+  void throwDeclaredRuntimeException(AsyncCallback<Void> callback);
+
+  void throwUnknownRuntimeException(AsyncCallback<Void> callback);
+}
=======================================
---  
/releases/2.0/user/test/com/google/gwt/user/server/rpc/RemoteServiceServletTestServiceImplBase.java
      
Mon Jul  6 16:17:17 2009
+++  
/releases/2.0/user/test/com/google/gwt/user/server/rpc/RemoteServiceServletTestServiceImplBase.java
      
Fri Nov 20 10:37:09 2009
@@ -25,6 +25,15 @@
   */
  public class RemoteServiceServletTestServiceImplBase extends
      HybridServiceServlet implements RemoteServiceServletTestService {
+
+  /**
+   * A RuntimeException the client code shouldn't know anything about.
+   */
+  public static class FooException extends RuntimeException {
+    public FooException() {
+      super("This is OK.  Simulating random backend code exception.");
+    }
+  }

    public void test() {
    }
@@ -46,4 +55,12 @@
            + getPermutationStrongName());
      }
    }
-}
+
+  public void throwDeclaredRuntimeException() {
+    throw new NullPointerException("expected");
+  }
+
+  public void throwUnknownRuntimeException() {
+    throw new FooException();
+  }
+}

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

Reply via email to