> But one of the main benefits of using the command pattern is that you > only need one client API call.
I guess it's a matter of opinion, but I always saw that as an implementation detail that gets exposed to any code that wants to use the RPC service. My goal in writing gwt-remote-action was to make it so that code on either side of the RPC call didn't need to know whether or not the command pattern is being used. I prefer to have only code that wants to deal with actions as opaque objects see them as such (e.g. see gwt-remote-action's merge<http://code.google.com/p/gwt-remote-action/source/browse/merge/src/main/java/com/google/code/gwt/remoteaction/merge/client/MergingActionFilter.java#30>and batch<http://code.google.com/p/gwt-remote-action/source/browse/batch/src/main/java/com/google/code/gwt/remoteaction/batch/client/AbstractBatchingActionFilter.java#28>filters), while code that just wants to make an RPC call simply calls a method. I forgot to link this in my previous mail, but to get a better feel for the gwt-remote-action usage see http://code.google.com/p/gwt-remote-action/wiki/UsageGuide. > On the server side you have one servlet that can use Guice to > inject the handlers so you can implement handlers as POJOs. This is still the case with gwt-remote-action (and is in fact possible with traditional GWT RPC - see here<http://stuffthathappens.com/blog/2009/09/14/guice-with-gwt/> and here <http://code.google.com/p/ggdi/>). > You should maybe look at something like gwt-dispatcher that implements > the command pattern. I've used gwt-dispatch <http://code.google.com/p/gwt-dispatch/> before, and it wasn't quite what I was looking for. I found adding a new action to be a bit harder than I'd like (adding an action, result, and handler class, as well as a Guice binding). This was actually my main inspiration for starting gwt-remote-action. I also thought that gwt-dispatch is fairly focused on transactional actions, which, while certainly an important use of the command pattern, isn't really what I was interested in. Cheers, Jamie On Thu, Nov 19, 2009 at 6:52 AM, David <[email protected]> wrote: > Hi, > > I follow what you are trying to do, it would solve some complexity on > the server side. > > But one of the main benefits of using the command pattern is that you > only need one client API call. In case of GWT it means that you write > onesync and async interface with one method... and from there on you > can add commands without having to update these interfaces. On the > server side you have one servlet that can use Guice to inject the > handlers so you can implement handlers as POJOs. That is easier for > testing and you don't need to extend the RPC servlet class everywhere. > > You should maybe look at something like gwt-dispatcher that implements > the command pattern. I don't like the fact that I need to define that > many classes (request,result, handler and registere the handler), but > I guess there can be solutions to improve upon that design as well > (introspection to find the handler automatically for example). > > > David > > On Thu, Nov 19, 2009 at 6:52 AM, Jamie Gennis <[email protected]> wrote: > > 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 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 (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: > > > > Make the ActionExecutionService use the new deRPC implementation rather > than > > the previous RPC implementation > > Use the RpcProxyCreator class to generate a dummy implementation of a > deRPC > > proxy for the action service (this proxy doesn't get used) > > 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 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 > > -- > http://groups.google.com/group/Google-Web-Toolkit-Contributors > -- http://groups.google.com/group/Google-Web-Toolkit-Contributors
