7 Tips for Exception Handling in GWT

June 11th, 2012 Ben Northrop, Consultant  (email the author)

This entry is part 10 of 11 in the series Developing GWT Applications

Exception handling best practices in Java are pretty straight-forward by now, and for the most part this carries over into the world of GWT (since GWT is, from the end developer’s perspective, just Java!). There are, however, a few tricks and gotchas that are important to be aware of. In this blog post I’ll describe a few that I’ve encountered, but please feel free to share your own.

Starting with the client tier and working my way to the server, here we go…

1. Handle all uncaught client-side exceptions

The first thing and simplest thing to do is to ensure that all unhandled exceptions bubbling out of the client tier are caught. You can accomplish this by registering an UncaughtExceptionHandler, like this:

  public class Example implements EntryPoint {
    public void onModuleLoad() {
      GWT.setUncaughtExceptionHandler(new 
        GWT.UncaughtExceptionHandler() {
        public void onUncaughtException(Throwable e) {
          // do exception handling stuf
      }
      // do module loading stuff
    }  
}

Pretty straight-forward. What happens if you don’t set the UncaughtExceptionHandler in your application? Well, in hosted mode, the default handler will conveniently log the exception and stack trace to the console. In web mode, however, the handler is null, and so exceptions “escape” to your browser, which is likely not the user experience you’re looking for (though it does provide the opportunity to use a Javascript debugger for further diagnosis).

Also, be aware that your custom UncaughtExceptionHandler only takes effect after the onModuleLoad() returns. In other words, if there was an exception raised where the comment “do module loading stuff” is in the example above, this exception would not be handled using your custom UncaughtExceptionHandler that you just defined. This is a known issue, and will not be resolved. As a work-around, if you expect exceptions could be thrown within onModuleLoads(), then you can extract the guts of onModuleLoad() to another method, and invoke it using a DeferredCommand:

public class Example implements EntryPoint {
  public void onModuleLoad() {
    GWT.setUncaughtExceptionHandler(new 
      GWT.UncaughtExceptionHandler() {
      public void onUncaughtException(Throwable e) {
        // do exception handling stuff
      }
    });
    DeferredCommand.addCommand(new Command() {
      public void execute() {
        onModuleLoad2();
      }
    });
  }

  private void onModuleLoad2() {
    // do module loading stuff
  }
}

2. Unravel GWT’s UmbrellaException

Once your UncaughtExceptionHandler is set, you may notice that some of the exceptions caught are instances of GWT’s UmbrellaException, rather than the actual exception thrown within your code. GWT uses UmbrellaExceptions to wrap exceptions in certain cases (loops, etc.), and it nests the root exception within it. For the purpose of easier debugging and more accurate error handling, it’s often helpful to unwrap these exceptions, like this:

GWT.setUncaughtExceptionHandler(new 
  GWT.UncaughtExceptionHandler() {
  public void onUncaughtException(Throwable e) {
    Throwable unwrapped = unwrap(e);
    // do exception handling stuff
  }  
  
  public Throwable unwrap(Throwable e) { 
    if(e instanceof UmbrellaException) { 
      UmbrellaException ue = (UmbrellaException) e;
      if(ue.getCauses().size() == 1) { 
        return unwrap(ue.getCauses().iterator().next());
      }
    }
    return e;
  }
}

3. Log exceptions on the client

Now that you have a handler catching your rogue client side exceptions and also a handy method to unravel them to their root causes, you’ll want to actually do something useful with these exceptions to help developers more easily diagnose and resolve the underlying problems. While the appropriate action may differ per application (e.g. present an error dialog, email the support team, etc.), at the very least the exception should be logged. Fortunately, GWT delivers a simple but powerful solution for client-side logging, piggy-backing off of the standard java.util.logging package. Plugging a logger into the UncaughtExceptionHandler is easily accomplished:

public class Example implements EntryPoint {

  private Logger logger = Logger.getLogger("");

  public void onModuleLoad() {
   GWT.setUncaughtExceptionHandler(new 
     GWT.UncaughtExceptionHandler() {
     public void onUncaughtException(Throwable e) {
        logger.log(Level.ERROR, "Ex caught!", e);
    }
    // do module loading stuff
  }  
}

Leveraging a helpful set of handlers that GWT provides, loggers can write to a number of different channels: development mode window, console, in-window pop-up, Firebug, and, the coup-de-gras, to the server(which I’ll cover in a bit). These channels are easily configurable via your .gwt.xml file:

  <inherits name="com.google.gwt.logging.Logging" />
  <set-property name="gwt.logging.logLevel" 
    value="INFO" />          
  <set-property name="gwt.logging.enabled" 
    value="TRUE" /> 
  <set-property name="gwt.logging.developmentModeHandler" 
    value="ENABLED" />  
  <set-property name="gwt.logging.systemHandler" 
    value="DISABLED" />
  <set-property name="gwt.logging.popupHandler" 
    value="DISABLED" />
  <set-property name="gwt.logging.consoleHandler" 
    value="ENABLED"/> 
  <set-property name="gwt.logging.firebugHandler" 
    value="ENABLED" />

Logging in GWT is very well documented, so there’s no need to retread here.

4. Log exceptions on the server

The majority of the logging handlers write to the client side, however in a production environment this often isn’t much help to you. When your users stumble upon an error, it’s unlikely (and actually unsafe from a security perspective!) to expect them to look in client-side log files to help you diagnose the problem. Instead, your application should do this work for you, by logging all unexpected exceptions to the server. Fortunately, remote logging of this sort is as simple as enabling it in the .gwt.xml config file:

  <set-property name="gwt.logging.simpleRemoteHandler" 
      value="ENABLED" />  

…and then defining the remote logging servlet in your web.xml file:

  
    remoteLogging
    com.google.gwt.logging.server.RemoteLoggingServiceImpl
  
  
    remoteLogging
    /your-gwt-module-name/remote_logging
  

Now, with one simple call to the log() method in your UncaughtExceptionHandler, the exception is sent automatically to your server log. Note, however, that by default it’s assumed that you’re using java.util.logging, which may not necessarily be the case. To hook these logs into, say, Log4J, check out SL4J.

Finally, it’s possible that instead of using GWT’s remote logging, you may want to implement a simple custom RPC to log (and possibly do other things – e.g. send email, etc.) when an unexpected exception is encountered. For example:

GWT.setUncaughtExceptionHandler(new 
  GWT.UncaughtExceptionHandler() {
  public void onUncaughtException(Throwable e) {
    myService.handleException(e);
  }
}

Seems straight-forward, right? Be careful! First, not all exceptions are serializable or follow the rules of serializable (ugh!). For example, GWT’s JavascriptException inherits Serializable, but doesn’t implement a public no-arg constructor, so when you try to send this over the wire via your RPC, a run-time Serialization exception is thrown. And since this exception would be thrown inside your exception handler, it would effectively swallowed. Second, the stack trace within an exception is transient, and so is lost from client to server (so if you need it on the server side, send it as a separate parameter). Which leads me to the next tip…

5. Consider logging exception stack traces

Of course knowing that an exception occurred isn’t usually helpful unless you know exactly where it was thrown from! On the server side we take this for granted, since stack traces are typically logged by default with our exceptions. On the client side with GWT side, however, it’s not so easy. When Java code gets compiled into Javascript, it is both shrunk and obfuscated. This is obviously useful for performance and security, but from the perspective of supporting applications, it makes things much trickier. For example, instead of seeing a stack trace with method/class names and line numbers, you might get something like this:

 Unknown.Jq(Unknown Source) 
 Unknown.Tm(Unknown Source) 
 Unknown.Sm(Unknown Source)

Not too helpful! Fortunately, there are different options for how GWT compiles into Javascript which provide different amounts of information for debugging. Of course nothing in life is free; the trade-offs are security and performance. If you wanted nice clean Javascript that resembles your Java code and that produces nice clean stack traces, then you can flip the GWT compiler “style” flag to either “pretty” or “detailed”, however this would obviously increase the size the Javascript (and thus take longer to download)…and any user could more easily grock your client side code. There’s a nice compromise though, which is to provide file names and/or line numbers in your stack traces, but to keep the source code obfuscated. This can be configured in your .gwt.xml file:

  <set-property name="compiler.stackMode" 
       value="emulated" />
  <set-configuration-property name="compiler.emulatedStack.recordLineNumbers" 
       value="true"/> 
  <set-configuration-property name="compiler.emulatedStack.recordFileNames" 
       value="true"/> 

Note, again, that this does increase the size of your Javascript bundle. For one application I worked on, I found that just turning on the emulatedStack increased the Javascript by 30%, and turning on emulatedStack plus line numbers increased it by 100% (i.e. doubled the size!). Depending on the performance requirements of your application, this may or may not be acceptable.

Finally, it’s helpful to know that even with the obfuscated stack traces, there is still hope for hunting down mysterious exceptions. What seems like jibberish is just obfuscated code, and you can manually translate the obfuscated method names (e.g. “Jq”, “Tm”, or “Sm” from the above example) to the Java method names using the symbolMaps file that GWT generates. If only there were a way to do this programmatically…

6. Use the StackTraceDeobfuscator

GWT provides a utility class called the StackTraceDeobfuscator which (as it advertises!) deobfuscates stack traces, translating the condensed, jibberish stack trace (like above) into a one that you would expect. If you’re using GWT’s remote logger, then it helpfully uses this by default, with one caveat: it knows where to find the symbolMaps file. GWT places this in the “WEB-INF/deploy/your-app/symbolMaps” directory, and you can instruct the RemoteLoggerService to look there accordingly:

<init-param>
  <param-name>symbolMaps</param-name>
  <param-value>your-app/symbolMaps</param-value</param-value>
</init-param>

You could also call the StackTraceDeobfuscator manually, if, for example, you had your own RPC service that handled exceptions in some custom way. I wrote a simple subclass of the StackTraceDeobfuscator, and called it like this:

public class LogServiceImpl extends LogService { 
  public void logError(Throwable t, String stackTrace) {
    StackTraceElement[] sts = stackTraceDeobfuscator.deobfuscateStackTrace(t.getStackTrace());
    t.setStackTrace(sts);

    LOG.logError("An error happened", t);
  }
}

7. Throw exceptions explicitly in RPC Services

Now, to the server side (which, thankfully, is much less tricky!). Imagine you have a simple RPC service that throws a custom exception, MyRuntimeException, which extends from RuntimeException. For example:

public class MyServiceImpl extends MyService { 
  public void foo() { 
    throw new MyRuntimeException();
  }
}

By Java’s rules, there is no obligation that you explicitly declare (either in the impl or interface) that foo throws MyRuntimeException. For the sake of simplicity, you may choose not to, however you might be surprised then to find that when you catch this exception in your AsyncCallback

myService.foo(new AsyncCallback<Void>() { 
  public void onSuccess(void v) {
  }

  public void onFailure(Throwable t) {
    if(t instanceof MyRuntimeException) { 
      // do something
    }
  }
}

…the object t will not be an instance of MyRuntimeException, but rather an UnexpectedException. This is because GWT did not know to compile MyRuntimeException into Javascript, and so the client code does not know of this type. To remedy, make sure you explicitly throw MyRuntimeException in your service interface (but not in your Async interface!):

public interface MyService { 
  void foo() throws MyRuntimeException;
}

Note that if you threw some subclass of MyRuntimeException, the instanceof check would work, however be aware that GWT would then compile all concrete subclasses of MyRuntimeException, which could slow build time.

Whew…that’s it. Please share any tips you have, or let me know if you see anything awry with what I’ve written. Thanks!

Be Sociable, Share!

Entry Filed under: Agile and Development

20 Comments Add your own

  • 1. Ashton  |  July 24th, 2012 at 10:54 am

    Great post, Ben!

    Some of our biggest exceptions are IncompatibleRemoteServiceException for when the app doesn’t get an update and is stale or InvocationException on the mobile app when someone losses internet connection.

    I would love to hear your thoughts on handling app updates while balance cacheing and performance.

    -AT

  • 2. Joel  |  July 24th, 2012 at 3:26 pm

    Ben, thanks for the great post.

    Just wanted to point out a typo in Tip 4. Log exceptions on the server.
    You’ve got

    where it ought to be

    Thanks again!

  • 3. Joel  |  July 24th, 2012 at 3:28 pm

    Grr, I failed at the HTML formatting. That should have said that in the first code snippet, you’re setting the property “gwt.logging.simpleRemoteHandler” to DISABLED when it should be ENABLED.

  • 4. Ben Northrop  |  July 30th, 2012 at 3:21 pm

    Thanks for the comments!

    @Joel – Fixed the typo.

    @Ashton – good call on the IncompatibleRemoteServiceException…and nice thought for a future post maybe.

  • 5. Kiran  |  August 1st, 2012 at 12:46 am

    Hi,

    Thanks.

    But there is a small bug in the method “public Throwable unwrap(Throwable e)”. Currently it keeps invoking itself with the same Throwable object, which causes StackOverflowException. I think here we need to invoke with the first cause. So the method will be

    public Throwable unwrap(Throwable e) {
    if(e instanceof UmbrellaException) {
    UmbrellaException ue = (UmbrellaException) e;
    if(ue.getCauses().size() == 1) {
    return unwrap(ue.getCauses().iterator().next());
    }
    }
    return e;
    }

  • 6. Ben Northrop  |  August 3rd, 2012 at 1:54 pm

    Kiran – Oh man…good catch…sorry about that! You are correct (and it is fixed now in the post). I obviously mis-wrote that method when I copied it over for the blog post. Thanks much for point that out.

  • 7. The Problem with GWT̵&hellip  |  August 6th, 2012 at 8:12 am

    [...] 3 time zones away from you, in New York. Seems like maybe this is the problem. You throw a few debug statements in your app, set the Time Zone on your local machine to Pacific, and sure enough, you recreate the [...]

  • 8. Javier  |  August 21st, 2012 at 5:02 am

    Great post. Really helpful

  • 9. Ravesh  |  September 12th, 2012 at 9:15 am

    Thanks Ben. Really helpful.

  • 10. Francesco  |  October 18th, 2012 at 11:18 am

    Thanks so much! This saved me a lot of time!

  • 11. Unable to deobfuscate GWT&hellip  |  October 31st, 2012 at 2:06 pm

    [...] some investigations, I found 7 Tips for Exception Handling in GWT and WebModeExceptions that contained valuable [...]

  • 12. Nick  |  January 25th, 2013 at 11:01 pm

    Thanks Ben!

    Its a great post, I have a question though, when ever an exception is thrown by my RPC service i get a generic stack trace which is not helpful. All my services throw java.lang.Exception. I also want to see the stack trace of what caused the exception in the service but i just see stack trace as:

    java.lang.NullPointerException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
    at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.instantiate(ServerSerializationStreamReader.java:914)
    at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.deserialize(ServerSerializationStreamReader.java:556)
    at com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamReader.readObject(AbstractSerializationStreamReader.java:119)
    at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader$ValueReader$8.readValue(ServerSerializationStreamReader.java:138)
    at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.deserializeValue(ServerSerializationStreamReader.java:385)
    at com.google.gwt.user.server.rpc.RPC.decodeRequest(RPC.java:303)
    at com.google.gwt.user.server.rpc.RemoteServiceServlet.processCall(RemoteServiceServlet.java:206)
    at com.google.gwt.user.server.rpc.RemoteServiceServlet.processPost(RemoteServiceServlet.java:248)
    at com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.doPost(AbstractRemoteServiceServlet.java:62)
    at com.cccis.gwt.common.controller.SpringGWTController.handleRequest(SpringGWTController.java:30)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

    As you can see in this stacktrace, i caused a nullpointerexception in the service. I want to see where it occured in the service (line no and method).

    Please advice,

    Thanks.

  • 13. Eugene  |  April 23rd, 2013 at 10:37 pm

    Ben: this is really good and informative post!

    Thanks a lot!

  • 14. Alex Epshteyn  |  July 18th, 2013 at 11:25 am

    This is a great blog post. I used it as a starting point for experimenting with GWT’s stack trace logging capability. As a result, I ended up rewriting the GWT compiler and libraries to do a much better job.

    You can read more about my efforts (which will hopefully make it into GWT some day) here: http://igg.me/at/gwt-stack-traces/x/3494291

  • 15. Pratik  |  September 14th, 2013 at 9:21 am

    Hello,

    First of all, this is really an awesome blog post and able to get many things out of it for GWT application.

    On top of the exception you described, we are continuously facing problem related to “Socket Exception: ” (Stack trace is mentioned at the end of the post).

    We have created the application in GWT 2.4, and running on Jboss 7.1. We are literally stuck up on why this error is comming up and what are the ways to resolve these error?

    We google lot many things but not able to find out the suitable post except yours, so earging to help us out to resolve this kind of error.

    What we observer till date is, user is able to send data from client to server using RPC and client’s internet connectivity are bed sometimes. So If we get this error, how we can handle/catch it on a Client Side and show him a proper message related to connectivity.

    Hoping to get the valuable feedback on the same.

    ——————————————————————————————————–

    Following are the some of the exception that we get from server log.

    Caused by: java.net.SocketException: Software caused connection abort: recv failed
    00:41:48,176 ERROR [stderr] (http–0.0.0.0-80-152) at java.net.SocketInputStream.socketRead0(Native Method)
    00:41:48,176 ERROR [stderr] (http–0.0.0.0-80-152) at java.net.SocketInputStream.read(SocketInputStream.java:150)
    00:41:48,176 ERROR [stderr] (http–0.0.0.0-80-152) at java.net.SocketInputStream.read(SocketInputStream.java:121)
    00:41:48,176 ERROR [stderr] (http–0.0.0.0-80-152) at java.io.DataInputStream.readFully(DataInputStream.java:195)
    00:41:48,176 ERROR [stderr] (http–0.0.0.0-80-152) at java.io.DataInputStream.readFully(DataInputStream.java:169)
    00:41:48,176 ERROR [stderr] (http–0.0.0.0-80-152) at net.sourceforge.jtds.jdbc.SharedSocket.readPacket(SharedSocket.java:842)
    00:41:48,176 ERROR [stderr] (http–0.0.0.0-80-152) at net.sourceforge.jtds.jdbc.SharedSocket.getNetPacket(SharedSocket.java:723)
    00:41:48,176 ERROR [stderr] (http–0.0.0.0-80-152) at net.sourceforge.jtds.jdbc.ResponseStream.getPacket(ResponseStream.java:466)
    00:41:48,176 ERROR [stderr] (http–0.0.0.0-80-152) at net.sourceforge.jtds.jdbc.ResponseStream.read(ResponseStream.java:103)
    00:41:48,176 ERROR [stderr] (http–0.0.0.0-80-152) at net.sourceforge.jtds.jdbc.ResponseStream.peek(ResponseStream.java:88)
    00:41:48,176 ERROR [stderr] (http–0.0.0.0-80-152) at net.sourceforge.jtds.jdbc.TdsCore.wait(TdsCore.java:3932)
    00:41:48,176 ERROR [stderr] (http–0.0.0.0-80-152) at net.sourceforge.jtds.jdbc.TdsCore.executeSQL(TdsCore.java:1046)

    Caused by: ClientAbortException: java.net.SocketException: Connection reset by peer: socket write error
    at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:403) [jbossweb-7.0.13.Final.jar:]
    at org.apache.tomcat.util.buf.ByteChunk.append(ByteChunk.java:354) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.connector.OutputBuffer.writeBytes(OutputBuffer.java:426) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.connector.OutputBuffer.write(OutputBuffer.java:415) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:89) [jbossweb-7.0.13.Final.jar:]
    at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:83) [jbossweb-7.0.13.Final.jar:]
    at com.google.gwt.user.server.rpc.RPCServletUtils.writeResponse(RPCServletUtils.java:330) [gwt-servlet.jar:]
    at com.google.gwt.user.server.rpc.RemoteServiceServlet.writeResponse(RemoteServiceServlet.java:357) [gwt-servlet.jar:]
    at com.google.gwt.user.server.rpc.RemoteServiceServlet.processPost(RemoteServiceServlet.java:256) [gwt-servlet.jar:]
    at com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.doPost(AbstractRemoteServiceServlet.java:62) [gwt-servlet.jar:]
    … 21 more

    We are on urge to find the solution of handling these kind of exceptions hindering RPCs(Whether be at client side or server side).

  • 16. Halil Karaköse  |  September 29th, 2013 at 1:58 pm

    Hi
    I think there is a type in 1st tip. You forgot “);” characters at the end of setUncaughtExceptionHandler line.

    public class Example implements EntryPoint {
    public void onModuleLoad() {
    GWT.setUncaughtExceptionHandler(new
    GWT.UncaughtExceptionHandler() {
    public void onUncaughtException(Throwable e) {
    // do exception handling stuf
    }); // << correct line
    // do module loading stuff
    }

  • 17. Enguerrand Dibanda  |  January 21st, 2014 at 3:54 pm

    Hi,

    great post, I found it is very helpfull!

    The DeferredCommand is however deprecated. It would be nice to update that part with the Scheduler API

    Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
    public void execute() {
    onModuleLoad2();
    }
    });

  • 18. Michael  |  February 22nd, 2014 at 7:47 am

    It works. But deobfuscation is not working with enabled closure comiler i.e., flag -XenableClosureCompiler.

    Do you have an idea how it works together?

  • 19. Nivedita  |  April 4th, 2014 at 6:17 am

    Hi All,

    I am facing com.google.gwt.core.client.JavaScriptException: (Error):
    description:
    number: -2146233088 in IE9 version.But this issue is not coming consistently.I am not able to figure it out that why this issue is coming .Please help me on the same ASAP.

  • 20. Santosh  |  April 10th, 2014 at 11:33 pm

    Hi,

    Is there a way to show server side exceptions such as ejb exception on smart gwt ui
    inside a modal window.

Leave a Comment

Required

Required, hidden


+ 3 = seven

Some HTML allowed:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Trackback this post  |  Subscribe to the comments via RSS Feed

© 2010-2014 Summa All Rights Reserved