Philippe, Kelsey - thanks for the very helpful responses. This sort of additional proxy behavior feels like something that would be of considerable value as a standard part of GWT. I'm surprised there aren't considerably more complaints in this group.
setServiceEntryPoint() was the magic button I was looking for. However, I ended up coming up with an alternative that fits my (heavily Guice-dependent) architecture a little better. Since your responses were so good (I wish I could give you stackoverflow points), I feel obligated explain what I did. This involves a larger number of artifacts than I would prefer, but it feels fairly natural to me: * I derived a new GuiceRemoteServiceServlet that overrides processCall() to look up the interface in the Guice injector and execute code on throwaway objects. * I created separate interface, interfaceAsync, and implementation classes for every single method. Each interface has a different @RemoteServiceRelativePath. Some of the impl classes handle multiple interfaces when there is appropriate shared logic. * I map the interfaces to implementation in GuiceConfig. * I created a Gin singleton that implements all the async interfaces and proxies to the individual gwt-rpc proxies; this keeps my client code blissfully ignorant of the complexity. It's a little painful to add an RPC method: * Create new interface and interfaceAsync * Implement the interface with a new impl class or on an existing impl class * Map the interface to impl in Guice * Add the async interface to the Gin singleton proxy On the other hand my biz logic is now the way I want it to be - wrapped up in small, modular, reusable, appropriately scoped, guice-injected chunks. Parameter/method changes refactor nicely with Eclipse. I could eliminate the mapping step with annotations if I was willing to accept the startup time penalty of classpath scanning, and I could probably even generate the Gin singleton if I wanted. But my remote interface doesn't change that often. This may not be for everyone, but for a DI addict like me it seems pretty reasonable. And my appengine logs are now beautiful again - current load, errors, and individual log entries are now broken down by method call. Jeff On Wed, Mar 30, 2011 at 4:33 AM, Kelsey Francis <[email protected]> wrote: > Jeff, > You could create a common subclass (let's call it JeffRemoteServiceServlet) > of RemoteServiceServlet that overrides the > onAfterRequestDeserialized(RPCRequest) method. That method is called, as the > name implies, everytime the servlet receives an RPC request, so it's the > perfect spot to collect information for logging. > That doesn't get you the timing information you're after, though, so you > could instead override RemoteServiceServlet.processCall(String) to > 1. Start a timer (i.e., record System.nanoTime() or whatever) > 2. Call super.processCall > 3. Stop the timer > Now, so long as all of your service implementations override > JeffRemoteServiceServlet, you've got the desired behavior everywhere. I'm > not sure where the best spot to do the actual averaging would be, but this > should at least let you hook in at the right spot. > Another (much more difficult) option, that you and Philippe have alluded to, > is to call setServiceEntryPoint client-side and pipe all of your RPC > requests through a single servlet. You can make this happen automatically, > or even completely take over the RPC transport process by creating a custom > proxy generator. We've done this, and as you mentioned, it's a little > difficult to wrap your head around at first, but once you have, the changes > are actually pretty minor (unless you decide to start adding features like > batching, etc.). You'd need: > 1. A new subclass of RemoteServiceProxy (let's call it > DispatchedRemoteServiceProxy) that overrides doInvoke() to take control of > the transport of the request > 2. A new subclass of ProxyCreator (let's call it > DispatchedRemoteProxyCreator) that overrides getProxySupertype() to > return DispatchedRemoteServiceProxy.class > 3. A new subclass of ServiceInterfaceProxyGenerator (let's call it > DispatchedRemoteServiceGenerator) that overrides createProxyCreator() to > return a new DispatchedRemoteProxyCreator > 4. Have your services inherit a new interface (DispatchedRemoteService) and > set up your .gwt.xml file generate impls of that interface with > DispatchedRemoteServiceGenerator. > The trickiest part is what to do in DispatchedRemoteServiceProxy.doInvoke(). > You'll want to make a request to your special dispatch servlet, of course, > and then decode it once you get a response. RequestCallbackAdapter holds the > keys to making this happen. Dispatching the request server-side is > relatively trivial once you have the decoded RPCRequest. > The point of all this is of course to have all your requests one through a > single servlet, which would, among other things, probably make collecting > information about requests a little easier. > -Kelsey > > -- > You received this message because you are subscribed to the Google Groups > "Google Web Toolkit" group. > To post to this group, send email to [email protected]. > To unsubscribe from this group, send email to > [email protected]. > For more options, visit this group at > http://groups.google.com/group/google-web-toolkit?hl=en. > -- You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/google-web-toolkit?hl=en.
