Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to wrap every Callback in one place to improve error handling

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?

like image 493
Niel de Wet Avatar asked Jul 01 '13 09:07

Niel de Wet


2 Answers

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:

  • DevGuideCodingBasicsDeferred.html
  • An example applied to performance tracking
like image 79
Niel de Wet Avatar answered Sep 21 '22 02:09

Niel de Wet


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...

like image 21
Chris Lercher Avatar answered Sep 22 '22 02:09

Chris Lercher