[seam-commits] Seam SVN: r9547 - trunk/src/remoting/org/jboss/seam/remoting/gwt.

seam-commits at lists.jboss.org seam-commits at lists.jboss.org
Tue Nov 11 02:24:02 EST 2008


Author: shane.bryzak at jboss.com
Date: 2008-11-11 02:24:02 -0500 (Tue, 11 Nov 2008)
New Revision: 9547

Added:
   trunk/src/remoting/org/jboss/seam/remoting/gwt/SeamRPCRequest.java
Removed:
   trunk/src/remoting/org/jboss/seam/remoting/gwt/GWT13Service.java
Modified:
   trunk/src/remoting/org/jboss/seam/remoting/gwt/GWTService.java
Log:
JBSEAM-2933

Deleted: trunk/src/remoting/org/jboss/seam/remoting/gwt/GWT13Service.java
===================================================================
--- trunk/src/remoting/org/jboss/seam/remoting/gwt/GWT13Service.java	2008-11-11 05:52:25 UTC (rev 9546)
+++ trunk/src/remoting/org/jboss/seam/remoting/gwt/GWT13Service.java	2008-11-11 07:24:02 UTC (rev 9547)
@@ -1,107 +0,0 @@
-package org.jboss.seam.remoting.gwt;
-
-import static org.jboss.seam.ScopeType.APPLICATION;
-import static org.jboss.seam.annotations.Install.BUILT_IN;
-
-import java.lang.reflect.Constructor;
-
-import org.jboss.seam.annotations.Create;
-import org.jboss.seam.annotations.Install;
-import org.jboss.seam.annotations.Name;
-import org.jboss.seam.annotations.Scope;
-import org.jboss.seam.annotations.intercept.BypassInterceptors;
-
-import com.google.gwt.user.client.rpc.SerializationException;
-import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
-import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter;
-
-/**
- * 
- * @author Shane Bryzak
- */
- at Scope(APPLICATION)
- at Name("org.jboss.seam.remoting.gwt.gwtRemoteService")
- at Install(value = false, precedence = BUILT_IN, classDependencies = {"com.google.gwt.user.client.rpc.RemoteService"})
- at BypassInterceptors
- at Deprecated
-public class GWT13Service extends GWTService
-{
-   private static final String SERIALIZABLE_TYPE_CLASS = "com.google.gwt.user.server.rpc.impl.ServerSerializableTypeOracleImpl";
-     
-   private Object serializableTypeOracle;
-   
-   private Constructor streamReaderConstructor;
-   private Constructor streamWriterConstructor;
-   
-   @Create
-   public void startup() throws Exception
-   {
-      try
-      {      
-         log.trace("GWT13Service starting up");
-         
-         Class serializableType = Class.forName(SERIALIZABLE_TYPE_CLASS);
-         String[] packagePaths = getPackagePaths();
-         Constructor typeConstructor = serializableType.getConstructor(new Class[] { packagePaths.getClass() });
-         
-         serializableTypeOracle = typeConstructor.newInstance((Object[]) packagePaths);
-         
-         streamReaderConstructor = ServerSerializationStreamReader.class.getConstructor(
-               new Class[] { serializableType } );
-         streamWriterConstructor = ServerSerializationStreamWriter.class.getConstructor(
-               new Class[] { serializableType } );
-      }
-      catch (Exception ex)
-      {
-         log.error("Error initializing GWT13Service.  Please ensure " +
-               "the GWT 1.3 libraries are in the classpath.");
-         throw ex;
-      }         
-   }
-
-   @Override
-   protected String createResponse(ServerSerializationStreamWriter stream,
-         Class responseType, Object responseObj, boolean isException)
-   {
-      stream.prepareToWrite();
-      if (responseType != void.class)
-      {
-         try
-         {
-            stream.serializeValue(responseObj, responseType);
-         } catch (SerializationException e)
-         {
-            responseObj = e;
-            isException = true;
-         }
-      }
-
-      return (isException ? "{EX}" : "{OK}") + stream.toString();     
-   }   
-   
-   @Override
-   public ServerSerializationStreamReader getStreamReader()
-   {
-      try
-      {
-         return (ServerSerializationStreamReader) streamReaderConstructor.newInstance(serializableTypeOracle);
-      }
-      catch (Exception ex) 
-      { 
-         throw new RuntimeException("Unable to create stream reader", ex);
-      }
-   }
-   
-   @Override
-   public ServerSerializationStreamWriter getStreamWriter()
-   {
-      try
-      {
-         return (ServerSerializationStreamWriter) streamWriterConstructor.newInstance(serializableTypeOracle);
-      }
-      catch (Exception ex)
-      {
-         throw new RuntimeException("Unable to create stream writer", ex);
-      }
-   }
-}

Modified: trunk/src/remoting/org/jboss/seam/remoting/gwt/GWTService.java
===================================================================
--- trunk/src/remoting/org/jboss/seam/remoting/gwt/GWTService.java	2008-11-11 05:52:25 UTC (rev 9546)
+++ trunk/src/remoting/org/jboss/seam/remoting/gwt/GWTService.java	2008-11-11 07:24:02 UTC (rev 9547)
@@ -1,59 +1,55 @@
 package org.jboss.seam.remoting.gwt;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.Arrays;
 import java.util.HashMap;
-import java.util.zip.GZIPOutputStream;
+import java.util.Map;
 
 import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.jboss.seam.core.ConversationPropagation;
 import org.jboss.seam.log.LogProvider;
 import org.jboss.seam.log.Logging;
-import org.jboss.seam.remoting.gwt.GWTToSeamAdapter.ReturnedObject;
 import org.jboss.seam.servlet.ContextualHttpServletRequest;
 import org.jboss.seam.web.AbstractResource;
 
-import com.google.gwt.user.client.rpc.SerializableException;
+import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
+import com.google.gwt.user.client.rpc.RemoteService;
 import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.server.rpc.RPC;
+import com.google.gwt.user.server.rpc.RPCRequest;
+import com.google.gwt.user.server.rpc.RPCServletUtils;
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+import com.google.gwt.user.server.rpc.SerializationPolicy;
+import com.google.gwt.user.server.rpc.SerializationPolicyLoader;
+import com.google.gwt.user.server.rpc.SerializationPolicyProvider;
+import com.google.gwt.user.server.rpc.UnexpectedException;
 import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
 import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter;
 
 /**
- * Abstract base class for GWT integration.
+ * Abstract base class for GWT 1.5 integration.
  * 
  * @author Shane Bryzak
  */
-public abstract class GWTService extends AbstractResource
+public abstract class GWTService extends AbstractResource implements SerializationPolicyProvider
 {
    protected static final LogProvider log = Logging.getLogProvider(GWTService.class);
-   
-   /*
-    * These members are used to get and set the different HttpServletResponse
-    * and HttpServletRequest headers.
-    */
-   private static final String ACCEPT_ENCODING = "Accept-Encoding";
-   private static final String CHARSET_UTF8 = "UTF-8";
-   private static final String CONTENT_ENCODING = "Content-Encoding";
-   private static final String CONTENT_ENCODING_GZIP = "gzip";
-   private static final String CONTENT_TYPE_TEXT_PLAIN_UTF8 = "text/plain; charset=utf-8";
-   private static final String GENERIC_FAILURE_MSG = "The call failed on the server; see server log for details";
-   private static final HashMap TYPE_NAMES;
 
-   /**
-    * Controls the compression threshold at and below which no compression will
-    * take place.
-    */
-   private static final int UNCOMPRESSED_BYTE_SIZE_LIMIT = 256;
+   private static final HashMap<String, Class<?>> TYPE_NAMES;
 
    static
    {
-      TYPE_NAMES = new HashMap();
+      TYPE_NAMES = new HashMap<String, Class<?>>();
       TYPE_NAMES.put("Z", boolean.class);
       TYPE_NAMES.put("B", byte.class);
       TYPE_NAMES.put("C", char.class);
@@ -62,59 +58,38 @@
       TYPE_NAMES.put("I", int.class);
       TYPE_NAMES.put("J", long.class);
       TYPE_NAMES.put("S", short.class);
+
    }
+   
+   /**
+    * A cache of moduleBaseURL and serialization policy strong name to
+    * {@link SerializationPolicy}.
+    */
+   private final Map<String, SerializationPolicy> serializationPolicyCache = new HashMap<String, SerializationPolicy>();
 
    @Override
    public String getResourcePath()
    {
       return "/gwt";
    }
-   
+
    protected abstract ServerSerializationStreamReader getStreamReader();
+
    protected abstract ServerSerializationStreamWriter getStreamWriter();
-   protected abstract String createResponse(ServerSerializationStreamWriter stream,
-         Class responseType, Object responseObj, boolean isException);
 
-   /**
-    * Return true if the response object accepts Gzip encoding. This is done by
-    * checking that the accept-encoding header specifies gzip as a supported
-    * encoding.
-    */
-   private static boolean acceptsGzipEncoding(HttpServletRequest request)
-   {
-      assert (request != null);
+   protected abstract String createResponse(
+         ServerSerializationStreamWriter stream, Class responseType,
+         Object responseObj, boolean isException);
 
-      String acceptEncoding = request.getHeader(ACCEPT_ENCODING);
-      if (null == acceptEncoding)
-      {
-         return false;
-      }
-
-      return (acceptEncoding.indexOf(CONTENT_ENCODING_GZIP) != -1);
-   }
-
-   /**
-    * This method attempts to estimate the number of bytes that a string will
-    * consume when it is sent out as part of an HttpServletResponse. This really
-    * a hack since we are assuming that every character will consume two bytes
-    * upon transmission. This is definitely not true since some characters
-    * actually consume more than two bytes and some consume less. This is even
-    * less accurate if the string is converted to UTF8. However, it does save us
-    * from converting every string that we plan on sending back to UTF8 just to
-    * determine that we should not compress it.
-    */
-   private static int estimateByteSize(final String buffer)
-   {
-      return (buffer.length() * 2);
-   }
-
    // private final Set knownImplementedInterfaces = new HashSet();
-   private final ThreadLocal perThreadRequest = new ThreadLocal();
+   private final ThreadLocal<HttpServletRequest> perThreadRequest = new ThreadLocal<HttpServletRequest>();
 
-   private final ThreadLocal perThreadResponse = new ThreadLocal();
+   private final ThreadLocal<HttpServletResponse> perThreadResponse = new ThreadLocal<HttpServletResponse>();
 
    /**
     * This is called internally.
+    * 
+    * @see RemoteServiceServlet#doPost
     */
    @Override
    public final void getResource(final HttpServletRequest request,
@@ -132,44 +107,38 @@
             public void process() throws Exception
             {
 
-               Throwable caught;
                try
                {
                   // Read the request fully.
                   //
-                  String requestPayload = readPayloadAsUtf8(request);
+                  String requestPayload = RemoteServiceServlet_readContent(request);
 
+                  RemoteServiceServlet_onBeforeRequestDeserialized(requestPayload);
+
                   // Invoke the core dispatching logic, which returns the
-                  // serialized
-                  // result.
-                  //
+                  // serialized result
                   String responsePayload = processCall(requestPayload);
 
+                  RemoteServiceServlet_onAfterResponseSerialized(responsePayload);
+
                   // Write the response.
                   //
-                  writeResponse(request, response, responsePayload);
-                  return;
-               } catch (IOException e)
-               {
-                  caught = e;
-               } catch (ServletException e)
-               {
-                  caught = e;
-               } catch (SerializationException e)
-               {
-                  caught = e;
+                  RemoteServiceServlet_writeResponse(request, response,
+                        responsePayload);
+
                } catch (Throwable e)
                {
-                  caught = e;
+                  RemoteServiceServlet_doUnexpectedFailure(e);
                }
 
-               respondWithFailure(response, caught);
             }
 
             @Override
             protected void restoreConversationId()
             {
-               // no conversation support for gwt requests
+               ConversationPropagation.instance().setConversationId(
+                     GWTService.this.perThreadRequest.get().getParameter(
+                           "conversationId"));
             }
 
             @Override
@@ -189,135 +158,420 @@
     */
    public String processCall(String payload) throws SerializationException
    {
-
-      // Let subclasses see the serialized request.
-      //
-      onBeforeRequestDeserialized(payload);
-
       // Create a stream to deserialize the request.
       //
-      ServerSerializationStreamReader streamReader = getStreamReader();
-      streamReader.prepareToRead(payload);
-
-      // Read the service interface
+      // ServerSerializationStreamReader streamReader = getStreamReader();
+      // streamReader.prepareToRead(payload);
       //
-      String serviceIntfName = streamReader.readString();
-
-      // Read the method name.
+      // // Read the service interface
+      // //
+      // String serviceIntfName = streamReader.readString();
       //
-      String methodName = streamReader.readString();
+      // // Read the method name.
+      // //
+      // String methodName = streamReader.readString();
+      //
+      // // Read the number and names of the parameter classes from the stream.
+      // // We have to do this so that we can find the correct overload of the
+      // // method.
+      // //
+      // int paramCount = streamReader.readInt();
+      // Class[] paramTypes = new Class[paramCount];
+      // for (int i = 0; i < paramTypes.length; i++)
+      // {
+      // String paramClassName = streamReader.readString();
+      // try
+      // {
+      // paramTypes[i] = getClassOrPrimitiveFromName(paramClassName);
+      // } catch (ClassNotFoundException e)
+      // {
+      // throw new SerializationException("Unknown parameter " + i
+      // + " type '" + paramClassName + "'", e);
+      // }
+      // }
+      //
+      // // Deserialize the parameters.
+      // //
+      // Object[] args = new Object[paramCount];
+      // for (int i = 0; i < args.length; i++)
+      // {
+      // args[i] = streamReader.deserializeValue(paramTypes[i]);
+      // }
 
-      // Read the number and names of the parameter classes from the stream.
-      // We have to do this so that we can find the correct overload of the
-      // method.
-      //
-      int paramCount = streamReader.readInt();
-      Class[] paramTypes = new Class[paramCount];
-      for (int i = 0; i < paramTypes.length; i++)
+      try
       {
-         String paramClassName = streamReader.readString();
-         try
-         {
-            paramTypes[i] = getClassOrPrimitiveFromName(paramClassName);
-         } catch (ClassNotFoundException e)
-         {
-            throw new SerializationException("Unknown parameter " + i
-                  + " type '" + paramClassName + "'", e);
-         }
+         SeamRPCRequest rpcRequest = RPC_decodeRequest(payload,
+               this.getClass(), this);
+
+         return RPC_invokeAndEncodeResponse(this, rpcRequest.getMethod(),
+               rpcRequest.getParameterTypes(), rpcRequest.getParameters(),
+               rpcRequest.getSerializationPolicy());
+      } catch (IncompatibleRemoteServiceException ex)
+      {
+         getServletContext()
+               .log(
+                     "An IncompatibleRemoteServiceException was thrown while processing this call.",
+                     ex);
+         return RPC.encodeResponseForFailure(null, ex);
       }
 
-      // Deserialize the parameters.
+      // Make the call via reflection.
       //
-      Object[] args = new Object[paramCount];
-      for (int i = 0; i < args.length; i++)
+      // String responsePayload = GENERIC_FAILURE_MSG;
+      // ServerSerializationStreamWriter streamWriter = getStreamWriter();
+      // Throwable caught = null;
+      // try
+      // {
+      // GWTToSeamAdapter.ReturnedObject returnedObject =
+      // adapter.callWebRemoteMethod(
+      // serviceIntfName, methodName, paramTypes, args);
+      // Class returnType = returnedObject.returnType;
+      // Object returnVal = returnedObject.returnedObject;
+      // // Class returnType = serviceIntfMethod.getReturnType();
+      // // Object returnVal = serviceIntfMethod.invoke(this, args);
+      // responsePayload = createResponse(streamWriter, returnType, returnVal,
+      // false);
+      // } catch (IllegalArgumentException e)
+      // {
+      // caught = e;
+      // } catch (IllegalAccessException e)
+      // {
+      // caught = e;
+      // } catch (InvocationTargetException e)
+      // {
+      // // Try to serialize the caught exception if the client is expecting it,
+      // // otherwise log the exception server-side.
+      // caught = e;
+      // Throwable cause = e.getCause();
+      // if (cause != null)
+      // {
+      // // Update the caught exception to the underlying cause
+      // caught = cause;
+      // // Serialize the exception back to the client if it's a declared
+      // // exception
+      // if (cause instanceof SerializableException)
+      // {
+      // Class thrownClass = cause.getClass();
+      // responsePayload = createResponse(streamWriter, thrownClass,
+      // cause, true);
+      // // Don't log the exception on the server
+      // caught = null;
+      // }
+      // }
+      // }
+      //
+      // if (caught != null)
+      // {
+      // responsePayload = GENERIC_FAILURE_MSG;
+      // ServletContext servletContext = getServletContext();
+      // // servletContext may be null (for example, when unit testing)
+      // if (servletContext != null)
+      // {
+      // // Log the exception server side
+      // servletContext.log("Exception while dispatching incoming RPC call",
+      // caught);
+      // }
+      // }
+   }
+
+   /**
+    * Gets the <code>HttpServletRequest</code> object for the current call. It
+    * is stored thread-locally so that simultaneous invocations can have
+    * different request objects.
+    */
+   protected final HttpServletRequest getThreadLocalRequest()
+   {
+      return perThreadRequest.get();
+   }
+
+   /**
+    * Gets the <code>HttpServletResponse</code> object for the current call. It
+    * is stored thread-locally so that simultaneous invocations can have
+    * different response objects.
+    */
+   protected final HttpServletResponse getThreadLocalResponse()
+   {
+      return perThreadResponse.get();
+   }
+
+   /**
+    * Returns an {@link RPCRequest} that is built by decoding the contents of an
+    * encoded RPC request and optionally validating that type can handle the
+    * request. If the type parameter is not <code>null</code>, the
+    * implementation checks that the type is assignable to the
+    * {@link com.google.gwt.user.client.rpc.RemoteService} interface requested
+    * in the encoded request string.
+    * 
+    * <p>
+    * If the serializationPolicyProvider parameter is not <code>null</code>, it
+    * is asked for a {@link SerializationPolicy} to use to restrict the set of
+    * types that can be decoded from the request. If this parameter is
+    * <code>null</code>, then only subtypes of
+    * {@link com.google.gwt.user.client.rpc.IsSerializable IsSerializable} or
+    * types which have custom field serializers can be decoded.
+    * </p>
+    * 
+    * <p>
+    * Invoking this method with <code>null</code> for the type parameter,
+    * <code>decodeRequest(encodedRequest, null)</code>, is equivalent to calling
+    * <code>decodeRequest(encodedRequest)</code>.
+    * </p>
+    * 
+    * @param encodedRequest
+    *           a string that encodes the
+    *           {@link com.google.gwt.user.client.rpc.RemoteService} interface,
+    *           the service method, and the arguments to pass to the service
+    *           method
+    * @param type
+    *           if not <code>null</code>, the implementation checks that the
+    *           type is assignable to the
+    *           {@link com.google.gwt.user.client.rpc.RemoteService} interface
+    *           encoded in the encoded request string.
+    * @param serializationPolicyProvider
+    *           if not <code>null</code>, the implementation asks this provider
+    *           for a {@link SerializationPolicy} which will be used to restrict
+    *           the set of types that can be decoded from this request
+    * @return an {@link RPCRequest} instance
+    * 
+    * @throws NullPointerException
+    *            if the encodedRequest is <code>null</code>
+    * @throws IllegalArgumentException
+    *            if the encodedRequest is an empty string
+    * @throws IncompatibleRemoteServiceException
+    *            if any of the following conditions apply:
+    *            <ul>
+    *            <li>if the types in the encoded request cannot be deserialized</li>
+    *            <li>if the {@link ClassLoader} acquired from
+    *            <code>Thread.currentThread().getContextClassLoader()</code>
+    *            cannot load the service interface or any of the types specified
+    *            in the encodedRequest</li>
+    *            <li>the requested interface is not assignable to
+    *            {@link com.google.gwt.user.client.rpc.RemoteService}</li>
+    *            <li>the service method requested in the encodedRequest is not a
+    *            member of the requested service interface</li>
+    *            <li>the type parameter is not <code>null</code> and is not
+    *            assignable to the requested
+    *            {@link com.google.gwt.user.client.rpc.RemoteService} interface
+    *            </ul>
+    */
+   public static SeamRPCRequest RPC_decodeRequest(String encodedRequest,
+         Class<?> type, SerializationPolicyProvider serializationPolicyProvider)
+   {
+      if (encodedRequest == null)
       {
-         args[i] = streamReader.deserializeValue(paramTypes[i]);
+         throw new NullPointerException("encodedRequest cannot be null");
       }
 
-      GWTToSeamAdapter adapter = GWTToSeamAdapter.instance();
+      if (encodedRequest.length() == 0)
+      {
+         throw new IllegalArgumentException("encodedRequest cannot be empty");
+      }
 
-      // Make the call via reflection.
-      //
-      String responsePayload = GENERIC_FAILURE_MSG;
-      ServerSerializationStreamWriter streamWriter = getStreamWriter();
-      Throwable caught = null;
+      ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+
       try
       {
-         ReturnedObject returnedObject = adapter.callWebRemoteMethod(
-               serviceIntfName, methodName, paramTypes, args);
-         Class returnType = returnedObject.returnType;
-         Object returnVal = returnedObject.returnedObject;
-         // Class returnType = serviceIntfMethod.getReturnType();
-         // Object returnVal = serviceIntfMethod.invoke(this, args);
-         responsePayload = createResponse(streamWriter, returnType, returnVal,
-               false);
-      } catch (IllegalArgumentException e)
-      {
-         caught = e;
-      } catch (IllegalAccessException e)
-      {
-         caught = e;
-      } catch (InvocationTargetException e)
-      {
-         // Try to serialize the caught exception if the client is expecting it,
-         // otherwise log the exception server-side.
-         caught = e;
-         Throwable cause = e.getCause();
-         if (cause != null)
+         ServerSerializationStreamReader streamReader = new ServerSerializationStreamReader(
+               classLoader, serializationPolicyProvider);
+         streamReader.prepareToRead(encodedRequest);
+
+         // Read the name of the RemoteService interface
+         String serviceIntfName = streamReader.readString();
+
+         /*
+          * todo?? if (type != null) { if (!implementsInterface(type,
+          * serviceIntfName)) { // The service does not implement the requested
+          * interface throw new IncompatibleRemoteServiceException(
+          * "Blocked attempt to access interface '" + serviceIntfName +
+          * "', which is not implemented by '" + printTypeName(type) +
+          * "'; this is either misconfiguration or a hack attempt"); } }
+          */
+
+         SerializationPolicy serializationPolicy = streamReader
+               .getSerializationPolicy();
+         Class<?> serviceIntf;
+         try
          {
-            // Update the caught exception to the underlying cause
-            caught = cause;
-            // Serialize the exception back to the client if it's a declared
-            // exception
-            if (cause instanceof SerializableException)
+            serviceIntf = RPC_getClassFromSerializedName(serviceIntfName,
+                  classLoader);
+            if (!RemoteService.class.isAssignableFrom(serviceIntf))
             {
-               Class thrownClass = cause.getClass();
-               responsePayload = createResponse(streamWriter, thrownClass,
-                     cause, true);
-               // Don't log the exception on the server
-               caught = null;
+               // The requested interface is not a RemoteService interface
+               throw new IncompatibleRemoteServiceException(
+                     "Blocked attempt to access interface '"
+                           + printTypeName(serviceIntf)
+                           + "', which doesn't extend RemoteService; this is either misconfiguration or a hack attempt");
             }
+         } catch (ClassNotFoundException e)
+         {
+            throw new IncompatibleRemoteServiceException(
+                  "Could not locate requested interface '" + serviceIntfName
+                        + "' in default classloader", e);
          }
-      }
 
-      if (caught != null)
-      {
-         responsePayload = GENERIC_FAILURE_MSG;
-         ServletContext servletContext = getServletContext();
-         // servletContext may be null (for example, when unit testing)
-         if (servletContext != null)
+         String serviceMethodName = streamReader.readString();
+
+         int paramCount = streamReader.readInt();
+         Class<?>[] parameterTypes = new Class[paramCount];
+
+         for (int i = 0; i < parameterTypes.length; i++)
          {
-            // Log the exception server side
-            servletContext.log("Exception while dispatching incoming RPC call",
-                  caught);
+            String paramClassName = streamReader.readString();
+            try
+            {
+               parameterTypes[i] = RPC_getClassFromSerializedName(
+                     paramClassName, classLoader);
+            } catch (ClassNotFoundException e)
+            {
+               throw new IncompatibleRemoteServiceException("Parameter " + i
+                     + " of is of an unknown type '" + paramClassName + "'", e);
+            }
          }
-      }
 
-      // Let subclasses see the serialized response.
-      //
-      onAfterResponseSerialized(responsePayload);
+         try
+         {
+            Method method = serviceIntf.getMethod(serviceMethodName,
+                  parameterTypes);
 
-      return responsePayload;
+            Object[] parameterValues = new Object[parameterTypes.length];
+            for (int i = 0; i < parameterValues.length; i++)
+            {
+               parameterValues[i] = streamReader
+                     .deserializeValue(parameterTypes[i]);
+            }
+
+            return new SeamRPCRequest(method, parameterValues, parameterTypes,
+                  serializationPolicy);
+
+         } catch (NoSuchMethodException e)
+         {
+            throw new IncompatibleRemoteServiceException(
+                  formatMethodNotFoundErrorMessage(serviceIntf,
+                        serviceMethodName, parameterTypes));
+         }
+      } catch (SerializationException ex)
+      {
+         throw new IncompatibleRemoteServiceException(ex.getMessage(), ex);
+      }
    }
 
    /**
-    * Gets the <code>HttpServletRequest</code> object for the current call. It
-    * is stored thread-locally so that simultaneous invocations can have
-    * different request objects.
+    * Returns the {@link Class} instance for the named class or primitive type.
+    * 
+    * @param serializedName
+    *           the serialized name of a class or primitive type
+    * @param classLoader
+    *           the classLoader used to load {@link Class}es
+    * @return Class instance for the given type name
+    * @throws ClassNotFoundException
+    *            if the named type was not found
     */
-   protected final HttpServletRequest getThreadLocalRequest()
+   private static Class<?> RPC_getClassFromSerializedName(
+         String serializedName, ClassLoader classLoader)
+         throws ClassNotFoundException
    {
-      return (HttpServletRequest) perThreadRequest.get();
+      Class<?> value = TYPE_NAMES.get(serializedName);
+      if (value != null)
+      {
+         return value;
+      }
+
+      return Class.forName(serializedName, false, classLoader);
    }
 
    /**
-    * Gets the <code>HttpServletResponse</code> object for the current call.
-    * It is stored thread-locally so that simultaneous invocations can have
-    * different response objects.
+    * Returns a string that encodes the result of calling a service method,
+    * which could be the value returned by the method or an exception thrown by
+    * it.
+    * 
+    * <p>
+    * If the serializationPolicy parameter is not <code>null</code>, it is used
+    * to determine what types can be encoded as part of this response. If this
+    * parameter is <code>null</code>, then only subtypes of
+    * {@link com.google.gwt.user.client.rpc.IsSerializable IsSerializable} or
+    * types which have custom field serializers may be encoded.
+    * </p>
+    * 
+    * <p>
+    * This method does no security checking; security checking must be done on
+    * the method prior to this invocation.
+    * </p>
+    * 
+    * @param target
+    *           instance on which to invoke the serviceMethod
+    * @param serviceMethod
+    *           the method to invoke
+    * @param args
+    *           arguments used for the method invocation
+    * @param serializationPolicy
+    *           determines the serialization policy to be used
+    * @return a string which encodes either the method's return or a checked
+    *         exception thrown by the method
+    * 
+    * @throws NullPointerException
+    *            if the serviceMethod or the serializationPolicy are
+    *            <code>null</code>
+    * @throws SecurityException
+    *            if the method cannot be accessed or if the number or type of
+    *            actual and formal arguments differ
+    * @throws SerializationException
+    *            if an object could not be serialized by the stream
+    * @throws UnexpectedException
+    *            if the serviceMethod throws a checked exception that is not
+    *            declared in its signature
     */
-   protected final HttpServletResponse getThreadLocalResponse()
+   public static String RPC_invokeAndEncodeResponse(Object target,
+         Method serviceMethod, Class[] paramTypes, Object[] args,
+         SerializationPolicy serializationPolicy) throws SerializationException
    {
-      return (HttpServletResponse) perThreadResponse.get();
+      if (serviceMethod == null)
+      {
+         throw new NullPointerException("serviceMethod");
+      }
+
+      if (serializationPolicy == null)
+      {
+         throw new NullPointerException("serializationPolicy");
+      }
+
+      String responsePayload;
+      try
+      {
+         GWTToSeamAdapter adapter = GWTToSeamAdapter.instance();
+
+         String serviceIntfName = serviceMethod.getDeclaringClass().getName();
+
+         GWTToSeamAdapter.ReturnedObject returnedObject = adapter
+               .callWebRemoteMethod(serviceIntfName, serviceMethod.getName(),
+                     paramTypes, args);
+
+         // Object result = serviceMethod.invoke(target, args);
+
+         responsePayload = RPC.encodeResponseForSuccess(serviceMethod,
+               returnedObject.returnedObject, serializationPolicy);
+      } catch (IllegalAccessException e)
+      {
+         SecurityException securityException = new SecurityException(
+               formatIllegalAccessErrorMessage(target, serviceMethod));
+         securityException.initCause(e);
+         throw securityException;
+      } catch (IllegalArgumentException e)
+      {
+         SecurityException securityException = new SecurityException(
+               formatIllegalArgumentErrorMessage(target, serviceMethod, args));
+         securityException.initCause(e);
+         throw securityException;
+      } catch (InvocationTargetException e)
+      {
+         // Try to encode the caught exception
+         //
+         Throwable cause = e.getCause();
+
+         responsePayload = RPC.encodeResponseForFailure(serviceMethod, cause,
+               serializationPolicy);
+      }
+
+      return responsePayload;
    }
 
    /**
@@ -325,7 +579,8 @@
     * returned to the client. The default implementation does nothing and need
     * not be called by subclasses.
     */
-   protected void onAfterResponseSerialized(String serializedResponse)
+   protected void RemoteServiceServlet_onAfterResponseSerialized(
+         String serializedResponse)
    {
    }
 
@@ -334,228 +589,388 @@
     * payload before it is deserialized into objects. The default implementation
     * does nothing and need not be called by subclasses.
     */
-   protected void onBeforeRequestDeserialized(String serializedRequest)
+   protected void RemoteServiceServlet_onBeforeRequestDeserialized(
+         String serializedRequest)
    {
    }
 
    /**
-    * Determines whether the response to a given servlet request should or
-    * should not be GZIP compressed. This method is only called in cases where
-    * the requestor accepts GZIP encoding.
-    * <p>
-    * This implementation currently returns <code>true</code> if the response
-    * string's estimated byte length is longer than 256 bytes. Subclasses can
-    * override this logic.
-    * </p>
+    * Override this method in order to control the parsing of the incoming
+    * request. For example, you may want to bypass the check of the Content-Type
+    * and character encoding headers in the request, as some proxies re-write
+    * the request headers. Note that bypassing these checks may expose the
+    * servlet to some cross-site vulnerabilities.
     * 
     * @param request
-    *           the request being served
-    * @param response
-    *           the response that will be written into
-    * @param responsePayload
-    *           the payload that is about to be sent to the client
-    * @return <code>true</code> if responsePayload should be GZIP compressed,
-    *         otherwise <code>false</code>.
+    *           the incoming request
+    * @return the content of the incoming request encoded as a string.
     */
-   protected boolean shouldCompressResponse(HttpServletRequest request,
-         HttpServletResponse response, String responsePayload)
+   protected String RemoteServiceServlet_readContent(HttpServletRequest request)
+         throws ServletException, IOException
    {
-      return estimateByteSize(responsePayload) > UNCOMPRESSED_BYTE_SIZE_LIMIT;
+      return RPCServletUtils.readContentAsUtf8(request, true);
    }
 
-   /**
-    * Returns the {@link Class} instance for the named class.
-    * 
-    * @param name
-    *           the name of a class or primitive type
-    * @return Class instance for the given type name
-    * @throws ClassNotFoundException
-    *            if the named type was not found
-    */
-   private Class getClassFromName(String name) throws ClassNotFoundException
+   public final SerializationPolicy getSerializationPolicy(
+         String moduleBaseURL, String strongName)
    {
-      return Class.forName(name, false, this.getClass().getClassLoader());
+
+      SerializationPolicy serializationPolicy = getCachedSerializationPolicy(
+            moduleBaseURL, strongName);
+      if (serializationPolicy != null)
+      {
+         return serializationPolicy;
+      }
+
+      serializationPolicy = doGetSerializationPolicy(getThreadLocalRequest(),
+            moduleBaseURL, strongName);
+
+      if (serializationPolicy == null)
+      {
+         // Failed to get the requested serialization policy; use the default
+         getServletContext()
+               .log(
+                     "WARNING: Failed to get the SerializationPolicy '"
+                           + strongName
+                           + "' for module '"
+                           + moduleBaseURL
+                           + "'; a legacy, 1.3.3 compatible, serialization policy will be used.  You may experience SerializationExceptions as a result.");
+         serializationPolicy = RPC.getDefaultSerializationPolicy();
+      }
+
+      // This could cache null or an actual instance. Either way we will not
+      // attempt to lookup the policy again.
+      putCachedSerializationPolicy(moduleBaseURL, strongName,
+            serializationPolicy);
+
+      return serializationPolicy;
    }
 
-   /**
-    * Returns the {@link Class} instance for the named class or primitive type.
-    * 
-    * @param name
-    *           the name of a class or primitive type
-    * @return Class instance for the given type name
-    * @throws ClassNotFoundException
-    *            if the named type was not found
-    */
-   private Class getClassOrPrimitiveFromName(String name)
-         throws ClassNotFoundException
+   private SerializationPolicy getCachedSerializationPolicy(
+         String moduleBaseURL, String strongName)
    {
-      Object value = TYPE_NAMES.get(name);
-      if (value != null)
+      synchronized (serializationPolicyCache)
       {
-         return (Class) value;
+         return serializationPolicyCache.get(moduleBaseURL + strongName);
       }
+   }
 
-      return getClassFromName(name);
+   private void putCachedSerializationPolicy(String moduleBaseURL,
+         String strongName, SerializationPolicy serializationPolicy)
+   {
+      synchronized (serializationPolicyCache)
+      {
+         serializationPolicyCache.put(moduleBaseURL + strongName,
+               serializationPolicy);
+      }
    }
 
    /**
-    * Obtain the special package-prefixes we use to check for custom serializers
-    * that would like to live in a package that they cannot. For example,
-    * "java.util.ArrayList" is in a sealed package, so instead we use this
-    * prefix to check for a custom serializer in
-    * "com.google.gwt.user.client.rpc.core.java.util.ArrayList". Right now, it's
-    * hard-coded because we don't have a pressing need for this mechanism to be
-    * extensible, but it is imaginable, which is why it's implemented this way.
+    * Gets the {@link SerializationPolicy} for given module base URL and strong
+    * name if there is one.
+    * 
+    * Override this method to provide a {@link SerializationPolicy} using an
+    * alternative approach.
+    * 
+    * @param request
+    *           the HTTP request being serviced
+    * @param moduleBaseURL
+    *           as specified in the incoming payload
+    * @param strongName
+    *           a strong name that uniquely identifies a serialization policy
+    *           file
+    * @return a {@link SerializationPolicy} for the given module base URL and
+    *         strong name, or <code>null</code> if there is none
     */
-   protected String[] getPackagePaths()
+   protected SerializationPolicy doGetSerializationPolicy(
+         HttpServletRequest request, String moduleBaseURL, String strongName)
    {
-      return new String[] { "com.google.gwt.user.client.rpc.core" };
-   }
+      // The request can tell you the path of the web app relative to the
+      // container root.
+      String contextPath = request.getContextPath();
 
-   private String readPayloadAsUtf8(HttpServletRequest request)
-         throws IOException, ServletException
-   {
-      int contentLength = request.getContentLength();
-      if (contentLength == -1)
+      String modulePath = null;
+      if (moduleBaseURL != null)
       {
-         // Content length must be known.
-         throw new ServletException("Content-Length must be specified");
+         try
+         {
+            modulePath = new URL(moduleBaseURL).getPath();
+         } catch (MalformedURLException ex)
+         {
+            // log the information, we will default
+            getServletContext().log(
+                  "Malformed moduleBaseURL: " + moduleBaseURL, ex);
+         }
       }
 
-      String contentType = request.getContentType();
-      boolean contentTypeIsOkay = false;
-      // Content-Type must be specified.
-      if (contentType != null)
+      SerializationPolicy serializationPolicy = null;
+
+      /*
+       * Check that the module path must be in the same web app as the servlet
+       * itself. If you need to implement a scheme different than this, override
+       * this method.
+       */
+      if (modulePath == null || !modulePath.startsWith(contextPath))
       {
-         // The type must be plain text or text/x-gwt-rpc
-         if (contentType.startsWith("text/plain") || contentType.startsWith("text/x-gwt-rpc"))
+         String message = "ERROR: The module path requested, "
+               + modulePath
+               + ", is not in the same web application as this servlet, "
+               + contextPath
+               + ".  Your module may not be properly configured or your client and server code maybe out of date.";
+         getServletContext().log(message);
+      } else
+      {
+         // Strip off the context path from the module base URL. It should be a
+         // strict prefix.
+         String contextRelativePath = modulePath
+               .substring(contextPath.length());
+
+         String serializationPolicyFilePath = SerializationPolicyLoader
+               .getSerializationPolicyFileName(contextRelativePath + strongName);
+
+         // Open the RPC resource file read its contents.
+         InputStream is = getServletContext().getResourceAsStream(
+               serializationPolicyFilePath);
+         try
          {
-            // And it must be UTF-8 encoded (or unspecified, in which case we
-            // assume that it's either UTF-8 or ASCII).
-            if (contentType.indexOf("charset=") == -1)
+            if (is != null)
             {
-               contentTypeIsOkay = true;
-            } else if (contentType.indexOf("charset=utf-8") != -1)
+               try
+               {
+                  serializationPolicy = SerializationPolicyLoader
+                        .loadFromStream(is, null);
+               } catch (ParseException e)
+               {
+                  getServletContext().log(
+                        "ERROR: Failed to parse the policy file '"
+                              + serializationPolicyFilePath + "'", e);
+               } catch (IOException e)
+               {
+                  getServletContext().log(
+                        "ERROR: Could not read the policy file '"
+                              + serializationPolicyFilePath + "'", e);
+               }
+            } else
             {
-               contentTypeIsOkay = true;
+               String message = "ERROR: The serialization policy file '"
+                     + serializationPolicyFilePath
+                     + "' was not found; did you forget to include it in this deployment?";
+               getServletContext().log(message);
             }
-         }
-      }
-      if (!contentTypeIsOkay)
-      {
-         throw new ServletException(
-               "Content-Type must be 'text/plain' or 'text/x-gwt-rpc' with 'charset=utf-8' (or unspecified charset)");
-      }
-      InputStream in = request.getInputStream();
-      try
-      {
-         byte[] payload = new byte[contentLength];
-         int offset = 0;
-         int len = contentLength;
-         int byteCount;
-         while (offset < contentLength)
+         } finally
          {
-            byteCount = in.read(payload, offset, len);
-            if (byteCount == -1)
+            if (is != null)
             {
-               throw new ServletException("Client did not send "
-                     + contentLength + " bytes as expected");
+               try
+               {
+                  is.close();
+               } catch (IOException e)
+               {
+                  // Ignore this error
+               }
             }
-            offset += byteCount;
-            len -= byteCount;
          }
-         return new String(payload, "UTF-8");
-      } finally
-      {
-         if (in != null)
-         {
-            in.close();
-         }
       }
+
+      return serializationPolicy;
    }
 
+   private void RemoteServiceServlet_writeResponse(HttpServletRequest request,
+         HttpServletResponse response, String responsePayload)
+         throws IOException
+   {
+      boolean gzipEncode = RPCServletUtils.acceptsGzipEncoding(request)
+            && shouldCompressResponse(request, response, responsePayload);
+
+      RPCServletUtils.writeResponse(getServletContext(), response,
+            responsePayload, gzipEncode);
+   }
+
    /**
-    * Called when the machinery of this class itself has a problem, rather than
-    * the invoked third-party method. It writes a simple 500 message back to the
-    * client.
+    * Override this method to control what should happen when an exception
+    * escapes the {@link #processCall(String)} method. The default
+    * implementation will log the failure and send a generic failure response to
+    * the client.
+    * <p/>
+    * 
+    * An "expected failure" is an exception thrown by a service method that is
+    * declared in the signature of the service method. These exceptions are
+    * serialized back to the client, and are not passed to this method. This
+    * method is called only for exceptions or errors that are not part of the
+    * service method's signature, or that result from SecurityExceptions,
+    * SerializationExceptions, or other failures within the RPC framework.
+    * <p/>
+    * 
+    * Note that if the desired behavior is to both send the GENERIC_FAILURE_MSG
+    * response AND to rethrow the exception, then this method should first send
+    * the GENERIC_FAILURE_MSG response itself (using getThreadLocalResponse),
+    * and then rethrow the exception. Rethrowing the exception will cause it to
+    * escape into the servlet container.
+    * 
+    * @param e
+    *           the exception which was thrown
     */
-   private void respondWithFailure(HttpServletResponse response,
-         Throwable caught)
+   protected void RemoteServiceServlet_doUnexpectedFailure(Throwable e)
    {
       ServletContext servletContext = getServletContext();
-      servletContext.log("Exception while dispatching incoming RPC call",
-            caught);
-      try
-      {
-         response.setContentType("text/plain");
-         response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-         response.getWriter().write(GENERIC_FAILURE_MSG);
-      } catch (IOException e)
-      {
-         servletContext
-               .log(
-                     "sendError() failed while sending the previous failure to the client",
-                     caught);
-      }
+      RPCServletUtils.writeResponseForUnexpectedFailure(servletContext,
+            getThreadLocalResponse(), e);
    }
 
-   private void writeResponse(HttpServletRequest request,
+   /**
+    * Determines whether the response to a given servlet request should or
+    * should not be GZIP compressed. This method is only called in cases where
+    * the requester accepts GZIP encoding.
+    * <p>
+    * This implementation currently returns <code>true</code> if the response
+    * string's estimated byte length is longer than 256 bytes. Subclasses can
+    * override this logic.
+    * </p>
+    * 
+    * @param request
+    *           the request being served
+    * @param response
+    *           the response that will be written into
+    * @param responsePayload
+    *           the payload that is about to be sent to the client
+    * @return <code>true</code> if responsePayload should be GZIP compressed,
+    *         otherwise <code>false</code>.
+    */
+   protected boolean shouldCompressResponse(HttpServletRequest request,
          HttpServletResponse response, String responsePayload)
-         throws IOException
    {
+      return RPCServletUtils
+            .exceedsUncompressedContentLengthLimit(responsePayload);
+   }
 
-      byte[] reply = responsePayload.getBytes(CHARSET_UTF8);
-      String contentType = CONTENT_TYPE_TEXT_PLAIN_UTF8;
+   private static String formatMethodNotFoundErrorMessage(Class<?> serviceIntf,
+         String serviceMethodName, Class<?>[] parameterTypes)
+   {
+      StringBuffer sb = new StringBuffer();
 
-      if (acceptsGzipEncoding(request)
-            && shouldCompressResponse(request, response, responsePayload))
+      sb.append("Could not locate requested method '");
+      sb.append(serviceMethodName);
+      sb.append("(");
+      for (int i = 0; i < parameterTypes.length; ++i)
       {
-         // Compress the reply and adjust headers.
-         //
-         ByteArrayOutputStream output = null;
-         GZIPOutputStream gzipOutputStream = null;
-         Throwable caught = null;
-         try
+         if (i > 0)
          {
-            output = new ByteArrayOutputStream(reply.length);
-            gzipOutputStream = new GZIPOutputStream(output);
-            gzipOutputStream.write(reply);
-            gzipOutputStream.finish();
-            gzipOutputStream.flush();
-            response.setHeader(CONTENT_ENCODING, CONTENT_ENCODING_GZIP);
-            reply = output.toByteArray();
-         } catch (UnsupportedEncodingException e)
-         {
-            caught = e;
-         } catch (IOException e)
-         {
-            caught = e;
-         } finally
-         {
-            if (null != gzipOutputStream)
-            {
-               gzipOutputStream.close();
-            }
-            if (null != output)
-            {
-               output.close();
-            }
+            sb.append(", ");
          }
+         sb.append(printTypeName(parameterTypes[i]));
+      }
+      sb.append(")'");
 
-         if (caught != null)
-         {
-            getServletContext().log("Unable to compress response", caught);
-            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-            return;
-         }
+      sb.append(" in interface '");
+      sb.append(printTypeName(serviceIntf));
+      sb.append("'");
+
+      return sb.toString();
+   }
+
+   private static String formatIllegalAccessErrorMessage(Object target,
+         Method serviceMethod)
+   {
+      StringBuffer sb = new StringBuffer();
+      sb.append("Blocked attempt to access inaccessible method '");
+      sb.append(getSourceRepresentation(serviceMethod));
+      sb.append("'");
+
+      if (target != null)
+      {
+         sb.append(" on target '");
+         sb.append(printTypeName(target.getClass()));
+         sb.append("'");
       }
 
-      // Send the reply.
+      sb.append("; this is either misconfiguration or a hack attempt");
+
+      return sb.toString();
+   }
+
+   private static String formatIllegalArgumentErrorMessage(Object target,
+         Method serviceMethod, Object[] args)
+   {
+      StringBuffer sb = new StringBuffer();
+      sb.append("Blocked attempt to invoke method '");
+      sb.append(getSourceRepresentation(serviceMethod));
+      sb.append("'");
+
+      if (target != null)
+      {
+         sb.append(" on target '");
+         sb.append(printTypeName(target.getClass()));
+         sb.append("'");
+      }
+
+      sb.append(" with invalid arguments");
+
+      if (args != null && args.length > 0)
+      {
+         sb.append(Arrays.asList(args));
+      }
+
+      return sb.toString();
+   }
+
+   /**
+    * Returns the source representation for a method signature.
+    * 
+    * @param method
+    *           method to get the source signature for
+    * @return source representation for a method signature
+    */
+   private static String getSourceRepresentation(Method method)
+   {
+      return method.toString().replace('$', '.');
+   }
+
+   /**
+    * Straight copy from
+    * {@link com.google.gwt.dev.util.TypeInfo#getSourceRepresentation(Class)} to
+    * avoid runtime dependency on gwt-dev.
+    */
+   private static String printTypeName(Class<?> type)
+   {
+      // Primitives
       //
-      response.setContentLength(reply.length);
-      response.setContentType(contentType);
-      response.setStatus(HttpServletResponse.SC_OK);
-      response.getOutputStream().write(reply);
+      if (type.equals(Integer.TYPE))
+      {
+         return "int";
+      } else if (type.equals(Long.TYPE))
+      {
+         return "long";
+      } else if (type.equals(Short.TYPE))
+      {
+         return "short";
+      } else if (type.equals(Byte.TYPE))
+      {
+         return "byte";
+      } else if (type.equals(Character.TYPE))
+      {
+         return "char";
+      } else if (type.equals(Boolean.TYPE))
+      {
+         return "boolean";
+      } else if (type.equals(Float.TYPE))
+      {
+         return "float";
+      } else if (type.equals(Double.TYPE))
+      {
+         return "double";
+      }
+
+      // Arrays
+      //
+      if (type.isArray())
+      {
+         Class<?> componentType = type.getComponentType();
+         return printTypeName(componentType) + "[]";
+      }
+
+      // Everything else
+      //
+      return type.getName().replace('$', '.');
    }
 
 }

Added: trunk/src/remoting/org/jboss/seam/remoting/gwt/SeamRPCRequest.java
===================================================================
--- trunk/src/remoting/org/jboss/seam/remoting/gwt/SeamRPCRequest.java	                        (rev 0)
+++ trunk/src/remoting/org/jboss/seam/remoting/gwt/SeamRPCRequest.java	2008-11-11 07:24:02 UTC (rev 9547)
@@ -0,0 +1,47 @@
+package org.jboss.seam.remoting.gwt;
+
+import com.google.gwt.user.server.rpc.SerializationPolicy;
+
+import java.lang.reflect.Method;
+
+/**
+ * @author Tomaz Cerar
+ * @version $Revision$
+ * @modifiedBy $Author$
+ * @modified $Date$
+ */
+public class SeamRPCRequest
+{
+   private final java.lang.reflect.Method method;
+   private final java.lang.Object[] parameters;
+   private final Class[] parameterTypes;
+   private final com.google.gwt.user.server.rpc.SerializationPolicy serializationPolicy;
+
+   public SeamRPCRequest(Method method, Object[] parameters,
+         Class[] parameterTypes, SerializationPolicy serializationPolicy) {
+      this.method = method;
+      this.parameters = parameters;
+      this.parameterTypes = parameterTypes;
+      this.serializationPolicy = serializationPolicy;
+   }
+
+   public Method getMethod()
+   {
+      return method;
+   }
+
+   public Object[] getParameters()
+   {
+      return parameters;
+   }
+
+   public Class[] getParameterTypes()
+   {
+      return parameterTypes;
+   }
+
+   public SerializationPolicy getSerializationPolicy()
+   {
+      return serializationPolicy;
+   }
+}




More information about the seam-commits mailing list