Error and Exception Handling
The error handling rules were a bit buggy in version 1.0. Version 1.1 fixes the errors. Version 2.0 makes error handling easier to manage and control. This page primarily documents 2.0, with exceptions noted for previous versions.
First some definitions:
- An errorHandler is used when something breaks, and we are sure of what's gone wrong. For example - if someone stops the app-server then DWR raises an error to say that it's got an HTTP error.
- A warningHandler is used when something has broken, but we're not 100% sure that this isn't normal. For example, it is possible with page transitions on Firefox to get an XHR callback after the XHR object has been killed. So XHR throwing an exception might be bad, or it might not depending on the circumstances. By default there is no warning handler, because unless things get hairy, you don't need to know about warnings. However sometimes it might be useful for debugging purposes.
- An exceptionHandler can be used to catch exceptions thrown by app-server code. If no exception handlers exist, the error handler is used. Exception handlers are new in 2.0. See below for more.
- There is also a textHtmlHandler that allows you to manage what happens when a DWR request receives a response that isn't Javascript. This generally means that a server session has timed out, so it is usual in a textHtmlHandler to redirect to a login screen. The textHtmlHandler handler is new in 2.0.
You set a global error handler like this:
dwr.engine.setErrorHandler(handler);
Prior to DWR 2.0 you had to use the old format. (This still works with version 2.0 however it is deprecated).
DWREngine.setErrorHandler(handler);
You can also specify call or batch level error or warning handlers. For example, in the call meta-data:
Remote.method(params, {
callback:function(data) { ... },
errorHandler:function(errorString, exception) { ... }
});
or, in batch meta-data:
dwr.engine.beginBatch();
Remote.method(params, function(data) { ... });
// Other remote calls
dwr.engine.endBatch({
errorHandler:function(errorString, exception) { ... }
});
Exceptions
DWR can marshall exceptions, and they will become errors in JavaScript land (they can't be thrown since this will probably be happening asynchronously).
For example, if we remote the following Java class:
public class Remote {
public String getData() {
throw new NullPointerException("message");
}
}
Then in Javascript we will see the following:
function errh(msg) {
alert(msg);
}
dwr.engine.setErrorHandler(errh);
Remote.getData(function(data) { alert(data); });
The result will be an alert dialog from the errh() error handler.
Exceptions are subject to the usual marshaling rules – that is nothing is converted without express permission. However, there is one deviation from this rule. If there is no marshaller in place for an exception, then a default MinimalistExceptionConverter is used. The MinimalistExceptionConverter tells the client that something has gone wrong (which the client could guess anyway, since the call did not work) but for security reasons, it does not say what broke.
When there is no exception converter specified for an exception, the exception object passed to the client looks like this:
{
javaClassName:'java.lang.Throwable',
message:'Error'
}
You can enable a more verbose exception handler with the following in dwr.xml:
<convert match="java.lang.Exception" converter="exception"/>
By default, this will attempt to convert everything that is part of the exception, so the converted exception might look like this:
{
javaClassName:'org.xml.sax.SAXParseException',
lineNumber:42,
publicId:'somePublicId',
message:'Missing >'
}
Clearly, permission will be needed to convert nested properties, which includes the stack trace. By default, there is no permission to convert StackTrace elements. It is a good idea to either expose only the information that you wish to expose (better for a live environment):
<convert match="java.lang.Exception" converter="exception"> <param name='include' value='message,lineNumber'/> </convert>
Or allow stack traces too, (which can be useful in development):
<convert match="java.lang.StackTraceElement" converter="bean"/>
Aside from the fact that this is obviously insecure, it can also be quite verbose, so it might be best to use this technique only when the browser and web-server are close together in network terms.
Coping with server session expiry
The primary purpose of textHtmlHandler is to allow you to cope with the server expiring your session and sending a login page in response to a DWR request without letting DWR handle the response at all.
The standard usage goes something like this:
dwr.engine.setTextHtmlHandler(function() {
window.alert("Your session has expired, please login again." );
document.location = '/mycontext/login';
});
From DWR version 2.0.3 it is possible to show the login page using a pop-up div, keeping the user on the current page. The textHtmlHandler function is passed a parameter which contains information about the failure. The data passed in the parameter is:
- status: HTTP response code
- responseText: The contents of the error page
- contentType: The MIME type of the reply
Example usage might be something like this:
dwr.engine.setTextHtmlHandler(function(pageData) {
// display pageData.responseText in a Lightbox
});
A fully working example would need to post the login from inside an iframe to allow the user to remain on the same page.
