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
