How to wrap every Callback in one place to improve error handling
Asked Answered
C

4

6

Throughout my GWT app there are many different async calls to the server, using many different services. In order to do better error handling I want to wrap all my callbacks so that I can handle exceptions like InvocationExceptions in one place. A super class implementing AsyncCallback isn't really an option because that would mean that I would have to modify every async call.

RpcServiceProxy#doCreateRequestCallback() looks like the method to override. Simple enough. I just can't see how to make GWT use my new class.

Another way to state the question would be

How do I make GWT use my own subclass of RpcServiceProxy?

Ciro answered 1/7, 2013 at 9:33 Comment(1)
I think creating a class that implements AsyncCallback is your easiest option even if it takes a bit of time to refactor.Chivalric
C
9

In order to wrap every AsynCallback<T> that is passed to any RemoteService you need to override RemoteServiceProxy#doCreateRequestCallback() because every AsynCallback<T> is handed in here before an RPC call happens.

Here are the steps to do so:

As @ChrisLercher alluded, you need to define your own Proxy Generator to step in every time a RemoteService proxy gets generated. Start by extending ServiceInterfaceProxyGenerator and overriding #createProxyCreator().

/**
 * This Generator extends the default GWT {@link ServiceInterfaceProxyGenerator} and replaces it in the
 * co.company.MyModule GWT module for all types that are assignable to
 * {@link com.google.gwt.user.client.rpc.RemoteService}. Instead of the default GWT {@link ProxyCreator} it provides the
 * {@link MyProxyCreator}.
 */
public class MyServiceInterfaceProxyGenerator extends ServiceInterfaceProxyGenerator {
    @Override
    protected ProxyCreator createProxyCreator(JClassType remoteService) {
        return new MyProxyCreator(remoteService);
    }
}

In your MyModule.gwt.xml make use of deferred binding to instruct GWT to compile using your Proxy Generator whenever it generates something of the type RemoteService:

<generate-with 
   class="com.company.ourapp.rebind.rpc.MyServiceInterfaceProxyGenerator">
    <when-type-assignable class="com.google.gwt.user.client.rpc.RemoteService"/>
</generate-with>

Extend ProxyCreator and override #getProxySupertype(). Use it in MyServiceInterfaceProxyGenerator#createProxyCreator() so that you can define the base class for all the generated RemoteServiceProxies.

/**
 * This proxy creator extends the default GWT {@link ProxyCreator} and replaces {@link RemoteServiceProxy} as base class
 * of proxies with {@link MyRemoteServiceProxy}.
 */
public class MyProxyCreator extends ProxyCreator {
    public MyProxyCreator(JClassType serviceIntf) {
        super(serviceIntf);
    }

    @Override
    protected Class<? extends RemoteServiceProxy> getProxySupertype() {
        return MyRemoteServiceProxy.class;
    }
}

Make sure both your MyProxyCreator and your MyServiceInterfaceProxyGenerator are located in a package that will not get cross-compiled by GWT into javascript. Otherwise you will see an error like this:

[ERROR] Line XX: No source code is available for type com.google.gwt.user.rebind.rpc.ProxyCreator; did you forget to inherit a required module?

You are now ready to extend RemoteServiceProxy and override #doCreateRequestCallback()! Here you can do anything you like and apply it to every callback that goes to your server. Make sure that you add this class, and any other class you use here, in my case AsyncCallbackProxy, to your client package to be cross-compiled.

/**
 * The remote service proxy extends default GWT {@link RemoteServiceProxy} and proxies the {@link AsyncCallback} with
 * the {@link AsyncCallbackProxy}.
 */
public class MyRemoteServiceProxy extends RemoteServiceProxy {
    public MyRemoteServiceProxy(String moduleBaseURL, String remoteServiceRelativePath, String serializationPolicyName,
                                 Serializer serializer) {
        super(moduleBaseURL, remoteServiceRelativePath, serializationPolicyName, serializer);
    }

    @Override
    protected <T> RequestCallback doCreateRequestCallback(RequestCallbackAdapter.ResponseReader responseReader,
                                                          String methodName, RpcStatsContext statsContext,
                                                          AsyncCallback<T> callback) {
        return super.doCreateRequestCallback(responseReader, methodName, statsContext, new AsyncCallbackProxy<T>(callback));
    }
}

References:

Ciro answered 4/7, 2013 at 15:34 Comment(0)
S
2

The type you're looking for is probably RemoteServiceProxy (not RpcServiceProxy), and I assume, that you should start with overriding the default binding in /com/google/gwt/user/RemoteService.gwt.xml (just copy the lines to your own .gwt.xml file and adjust):

<generate-with 
       class="com.google.gwt.user.rebind.rpc.ServiceInterfaceProxyGenerator">
    <when-type-assignable class="com.google.gwt.user.client.rpc.RemoteService"/>
</generate-with>

There you'll find protected Class<? extends RemoteServiceProxy> getProxySupertype(), which you can override to return your own RemoteServiceProxyclass.

Haven't tried it yet, so this may need a few additional steps...

Statvolt answered 1/7, 2013 at 15:38 Comment(1)
Thanks Chris, that's the direction I also took. I'll add the missing steps in my answer.Ciro
J
0

Normally the way in GWT to handle exceptions happening in async processes is via UncaughtExceptionHandlers.

I would use my own handler to manage those exceptions:

GWT.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
  public void onUncaughtException(Throwable e) {
    if (e instanceof WhateverException) {
      // handle the exception here
    }        
  }
});

Using this you dont need to subclass anything.

Jana answered 1/7, 2013 at 15:39 Comment(2)
Unless I a completely confused about how GWT's RPCs work, those exceptions get handed to AsyncCallback#onFailure(Throwable), which means that they are not "uncaught" and will therefore not reach that handler. Only unhandled exceptions in the client code get handled there, like a null pointer exception in a widget, for example.Ciro
You are very right, only certain things use the uncaught exception approach like RequestFactory which is what I use. In a first view I thought RPC was the same.Palliate
C
0

If by "one place" you mean "I want to handle all errors in one method", then I would suggest either catching and throwing stuff until they're in one place OR creating an EventBus that you basically just send every error to. Then you can just have a single handler attached to this bus that can handle everything.

Clod answered 1/7, 2013 at 22:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.