Hi all,

I've been writing a GWT library to implement the command pattern for GWT RPC
calls using the same interfaces that traditional GWT RPC uses.  Basically,
when an app calls a method of the FooAsync action service interface, an
action object is created from the method arguments, processed by some action
filters on the client, then passed to the server.  The server then pulls the
method arguments out of the action object and passes them as arguments to
the appropriate method of the Foo interface implementation class.  The
return value (or thrown Throwable) from that method then gets put into an
action result object on the server and sent back to the client, where it
gets processed by filters and eventually has the return value or throwable
pulled out and passed to the onSuccess or onFailure methods of the async
callback.

I have a working (albeit not extensively tested)
[email protected] at
http://code.google.com/p/gwt-remote-action/, but the implementation relies
on some GWT internals, so it's rather brittle at the moment. I'd like to get
some feedback on how I can change my implementation to be more future-proof
(or correct if it isn't), and which (if any) of the GWT internals that I'm
using might be candidates for becoming a part of GWT's public interface.

The tricky part of the implementation is the action result.  The purpose of
an action result object is to encapsulate the result of executing an action
in such a way that action filters can manipulate it without having any
knowledge of what the action was.  It needs to contain an arbitrary return
value object or an arbitrary Throwable object (as well as an indication that
the Throwable was thrown rather than returned).  These objects are exposed
via the 
ActionResult<http://code.google.com/p/gwt-remote-action/source/browse/core/src/main/java/com/google/code/gwt/remoteaction/client/ActionResult.java>
interface.
 Simply having a general action result class that contains a return value
field of type Object and an exception field of type Throwable is not
desirable because it would cause deserialization code for every class that
GWT knows about to be included in the compiled javascript.

The current implementation works by generating one action result class for
each RPC method.  Identical action result classes are generated by a GWT
Generator at gwtc-time (for the client) and via Java byte code generation at
run-time (for the server).  My original idea was that this method-specific
action result class could have a result field of the specific return type of
the method along with a field for each type in the method's throws list.
 This turned out not to be necessary (at least for the cases I've tested),
because when GWT calls the Generator for the
ActionExecutionService<http://code.google.com/p/gwt-remote-action/source/browse/core/src/main/java/com/google/code/gwt/remoteaction/client/ActionExecutionService.java>
(the
traditional GWT RPC that sends the action objects to the server) it doesn't
find any of the generated method-specific action result classes.  Because of
this I simplified the action result classes to just have one field of type
Throwable, and I verified that not all the Throwable classes get included in
the deserializable classes list in the *.rpc.log files.  I'm not sure that
this behavior doesn't depend on GWT.create being called in a specific order
or something else I'm not aware of, but that should be solvable by splitting
the throwables into multiple fields if needed.

Generating the action result classes after the ActionExecutionService gets
generated means that none of the deserializers for the return values and
throwables of the action methods get generated or rescued.  To get around
this I:

   1. Make the ActionExecutionService use the new deRPC implementation
   rather than the previous RPC implementation
   2. Use the RpcProxyCreator class to generate a dummy implementation of a
   deRPC proxy for the action service (this proxy doesn't get used)
   3. Add an @ArtificialRescue to the generated action service proxy to
   rescue the _TypeOverridesFactory class that the RpcProxyCreator generated.
    This _TypeOverridesFactory class has all of the @ArtificalRescue's
   necessary to deserialize the fields of the generated action result class.

Step 1 is mostly acceptable to me, though it would be nice if my
implementation didn't depend upon an experimental feature that may not make
it into GWT 2.0.  Step 2 is somewhat wasteful since it takes gwtc time
generating a proxy class that it will ultimately throw away via dead code
elimination.  Step 3 is pretty gross since the action proxy generator
basically just divines the name of the _TypeOverridesFactory via string
manipulation (see the
getArtificialRescue<http://code.google.com/p/gwt-remote-action/source/browse/core/src/main/java/com/google/code/gwt/remoteaction/rebind/RemoteActionProxyCreator.java#359>
method).
 It may make sense to factor the _TypeOverridesFactory generation into its
own public *Creator class that could return the generated class name (or
maybe there's a better way for me to leverage that functionality).  Also,
the comments for the ArtificialRescue class make me feel like a bad person
for using it, so  if there's a better way to achieve what I'm trying to do
(or could be in the future with some GWT changes) let me know.

The one last bit of black magic required to make this work has to do with
GWT's aggressive dead code elimination.  The action service proxy class that
gets generated has code that essentially does:

Throwable thrown = actionResult.getException();
if (thrown != null)
  callback.onFailure(thrown);
else
  callback.onSuccess(actionResult.getResult())

However, because the field returned from the getException method never gets
written (except by the eval'd code returned from the deRPC call), the GWT
compiler thinks it can eliminate the whole if-block, resulting in an
unconditional call to callback.onSuccess.  I'm not sure whether this is
specific to how I'm doing things or if this is a bug with the deRPC code in
general.  I currently work around it by creating a dummy action result
object (of the generated action result class type) and I set the field
returned to 'new Exception()'.  This currently tricks the compiler into
thinking that the field gets written to a non-null value, but I don't have
too much faith that it will continue to trick it in the future.  Who knows
how smart that compiler will get?

If you've gotten this far then... Wow, I'm impressed!  If you actually
followed what I was trying to explain then I'm even more impressed.  I'd
love to hear what more GWT-knowledgeable folk than me think about this
implementation.  I think being able to add command pattern capabilities to
existing GWT RPC services is pretty cool, but if it can't be implemented in
a robust and maintainable way then it probably won't fly.

Thanks,
Jamie

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

Reply via email to