GWT RPC possible in window closing handler? - gwt

I'm listening for a window close event:
closeHandlerReg = Window.addCloseHandler(new CloseHandler<Window>() {
#Override
public void onClose(CloseEvent<Window> event) {
// ...
}
});
The documentation says that no UI can be shown in that callback. What about GWT RPC calls? I'm trying to make one, but it isn't showing up on the server (either in breakpoints or log statements).

Problem is that GWT RPC is asynchronous and the calls to RPC services return immediately. In this case the window is closed before the browser has a chance to send the underlying XMLHTTPRequest.
If it's absolutely necessary you should be able to hand-craft calling some sort of servlet (not GWT RPC) with a "synchronous" XMLHTTPRequest. Have a look here for an example: http://weblogs.asp.net/bleroy/archive/2005/12/15/433278.aspx
But really you shouldn't be doing anything of this sort in the window.onunload or window.onbeforeunload (these are the underlying DOM events for CloseEvent and ClosingEvent for the Window. Perhaps there is a better way to do what you are trying to do.

The use case you have should be possible. When you make the RPC call in the closeHandler it should arrive at the server, because while it returns directly, it has started sending the data, and set a callback to wait for the result. However the callback will fail because the connection is lost because the window is closed. But that is no problem as you only want to notify the server. So the question might be what are you sending? and does it work at all, when you send it at some other point in the code, not in the closeHandler?

Old question, but still - maybe someone faces the same issue.
RPCs won't work in the closing handler as already discussed. This workaround worked for me:
In the onClose method, do something like:
Window.Location.replace(GWT.getModuleBaseURL() + "rpcCall?param1=" + param1 + "&param2=" + param2);
whereas "rpcCall" is the name of the rpc url you have set in your web.xml file. Of course, a random number of parameters may be passed within the URL.
Then, in your server-side implementation of your rpc interface, you can override the doGet method:
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response) {
String param1 = URLDecoder.decode(request.getParameter("param1"), "UTF-8");
String param2 = URLDecoder.decode(request.getParameter("param2"), "UTF-8");
// do something
}

And another solution: Don't do an rpc call on window closing, but a regular http call that can then be handled by a custom servlet on the server side.
In your GWT module, do something like this:
Window.addWindowClosingHandler(new ClosingHandler() {
#Override
public void onWindowClosing(ClosingEvent event) {
sendWindowClosed(GWT.getModuleBaseURL() + "teardownservice");
}
});
private native void sendWindowClosed(String url)
/*-{
var Http = new XMLHttpRequest();
Http.open("GET", url);
Http.send();
}-*/;
}
In the server-side servlet, you can then handle this call:
public class TearDownServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
.... whatever you want ....
}
}

Related

Dumping bad requests

I have a service implemented with Dropwizard and I need to dump incorrect requests somewhere.
I saw that there is a possibility to customise the error message by registering ExceptionMapper<JerseyViolationException>. But I need to have the complete request (headers, body) and not only ConstraintViolations.
You can inject ContainerRequest into the ExceptionMapper. You need to inject it as a javax.inject.Provider though, so that you can lazily retrieve it. Otherwise you will run into scoping problems.
#Provider
public class Mapper implements ExceptionMapper<ConstraintViolationException> {
#Inject
private javax.inject.Provider<ContainerRequest> requestProvider;
#Override
public Response toResponse(ConstraintViolationException ex) {
ContainerRequest request = requestProvider.get();
}
}
(This also works with constructor argument injection instead of field injection.)
In the ContainerRequest, you can get headers with getHeaderString() or getHeaders(). If you want to get the body, you need to do a little hack because the entity stream is already read by Jersey by the time the mapper is reached. So we need to implement a ContainerRequestFilter to buffer the entity.
public class EntityBufferingFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
ContainerRequest request = (ContainerRequest) containerRequestContext;
request.bufferEntity();
}
}
You might not want this filter to be called for all requests (for performance reasons), so you might want to use a DynamicFeature to register the filter just on methods that use bean validation (or use Name Binding).
Once you have this filter registered, you can read the body using ContainerRequest#readEntity(Class). You use this method just like you would on the client side with Response#readEntity(). So for the class, if you want to keep it generic, you can use String.class or InputStream.class and convert the InputStream to a String.
ContainerRequest request = requestProvider.get();
String body = request.readEntity(String.class);

GWT RPC callback not executing within onModuleLoad

I have embedded a GWT RPC call within onModuleLoad method and this RPC call does not seem to be executing the onSuccess method. Within the RPC implementation at the server side it works well with debug print statements.
Code snippet below,
public void onModuleLoad() {
System.out.println("ON MODULE LOAD");
mobiTeamService.login(GWT.getHostPageBaseURL(), new AsyncCallback<LoginInfo> () {
public void onFailure(Throwable error) { System.out.println("FAILURE"); ClientSideUtils.logError(mobiTeamService, CLS,error) ;}
public void onSuccess(LoginInfo result) {
System.out.println("ON SUCCESS: " + result) ;
loginInfo = result ;
}
}) ;
}
Ensure that you have done the below steps (GWT-RPC), if so then the onSuccess will be definitely executed.
Interface(MobiTeamService) should be extending RemoteService(com.google.gwt.user.client.rpc.RemoteService)
AsynInterface(MobiTeamServiceAsync) should be in the same package where your interface resides.
MobiTeamServiceImpl should be extending RemoteServiceServlet and implementing MobiTeamService interface
Map your MobiTeamServiceImpl servlet in the web.xml, a servlet entry and its mapping
If you have specific path for accessing the service, then the path defined on the interface(MobiTeamService) by tag RemoteServiceRelativePath should be matching with the url pattern entry in the web.xml
You can also go thru this tutorial to get more understanding on GWT-RPC
http://www.gwtproject.org/doc/latest/tutorial/RPC.html

GWT JSNI method call failing, but there are no errors

I'm trying to implement Mozilla's Persona in a GWT App. Here's part of the code from a dummy app I set up to test it:
public class OpenId implements EntryPoint {
private native void callWatch(String email)
/*-{
$wnd.navigator.id.watch({
loggedInUser: email,
onlogin: function(assertion){
$wnd.alert("Calling method");
this.#com.gallup.openid.client.OpenId::processLogin(Ljava/lang/String;)(assertion);
$wnd.alert("Called Java Method");
},
onlogout: function(){alert("Logged Out!");}
});
}-*/;
private void processLogin(String assertion){
Window.alert("Logged in!");
personaStatus.setText("Log In Complete.");
}
}
When I call the callWatch method, only the "Calling method" alert box shows up. Neither of the other ones are ever called. So for some reason the code appears to be stopping at the JSNI call right below the first alert. But there are no errors in Dev Mode.
I don't understand why the processLogin method doesn't get called.
I thought I followed Google's Documentation correctly.
I did try writing
this.#com.gallup.openid.client.OpenId::processLogin(Ljava/lang/String;)(assertion);
as OpenID.#... and instance.#... due to this post.
I'm not sure what else to try.
The variable this points to the function that immediately surrounds it, which is in this case your onlogin JavaScript function. You need to use a temporary that variable (a typical JavaScript idiom, by the way)
private native void callWatch(String email)
/*-{
var that = this;
...
onlogin: function(assertion){
that.#com...
And then, ideally use $entry(...), so you will see error messages, if you have registered an UncaughtExceptionHandler.
See also: https://stackoverflow.com/a/5235580/291741

StatusCodeException Vs. RuntimeException in GWT

In my GWT app. I overrode RemoteServiceServlet to check if the session is valid right before the service method is being called. I am trying to throw a RuntimeException("expired session") from the server and I would like the client to catch this exception from the asynccallback onFailure...
In the client I would like to:
Asynccallback:
#Override
public void onFailure(Throwable caught) {
final String message = caught.getMessage();
if (!isNullOrEmptyString(message) && message.contains("expired session")) {
com.google.gwt.user.client.Window.Location.reload();
}
}
However, in the client, the caught object is still a StatusCodeException and the message is still the default "...Exception in the server...". how can I override the exception at least the default message to compare if it was a session expired message I sent from the server?
thanks
Hi Gursel,
Here's my code:
-> Custom RemoteServiceServlet. I'm trying to "intercept" every method before it's invoked. I check the session and throw a RuntimeException if it's already expired. So basically, it is not the declared method that throws the exception but the custom RemoteServiceServlet. It still goes to the "onFailure" in the client async but the Throwable object is still of type "StatusCodeException" without the EXPIRED_SESSION_MSG message. Don;t know how to make this work. Thanks!
public class XRemoteServiceServlet extends RemoteServiceServlet {
private final static String EXPIRED_SESSION_MSG = "ERROR: Application has expired session.";
#Override
protected void onAfterRequestDeserialized(RPCRequest rpcRequest) {
HttpServletRequest httpServletRequest = this.getThreadLocalRequest();
HttpSession session = httpServletRequest.getSession(false);
if (session != null) {
final String sessionIdFromRequestHeader = getSessionIdFromHeader();
if (!isNullOrEmptyString(sessionIdFromRequestHeader)) {
final String sessionId = session.getId();
if (!sessionId.equals(sessionIdFromRequestHeader)) {
throw new RuntimeException(EXPIRED_SESSION_MSG);
}
}
All RuntimeExceptions thrown by Server side of gwt application has been wrapped as StatusCodeException if you did not declare them at your remote method declaration.
EDIT :
After, Thomas Broyer comment, I have learned that all exceptions (checked or unchecked) that are declared at remote method declaration are propagated to gwt client. Therefore all you have to do is just declare your remote method such as :
public void myRemoteMethod() throws RuntimeException;
The post looks too old, still here is the solution I have come up with. Override processCall(String payload) of RemoveServiceServlet, if the session is invalid execute the blow code else call super.processCall(payload).
//Pass the exception to Client
RPCRequest rpcRequest = RPC.decodeRequest(payload, this.getClass(), this);
onAfterRequestDeserialized(rpcRequest);
return RPC.encodeResponseForFailure(rpcRequest.getMethod(), new CustomException("Invalid Session!"),rpcRequest.getSerializationPolicy(), rpcRequest.getFlags());
All the GWT services are servlets so GWT serializes the custom exception and sends to client as string, we follow the same :)

Gwt RequestFactory: editing a proxy immediately after receiving it

I want to RequestContext.edit(anObject) immediately after I receive it in Receiver.onSuccess, so that I can put it in my client-side database as already editable. Unfortunately, when I do so, RequestFactory complains that a request is already in progress. How can I achieve this?
requestContext.findOrganization(id).fire(new Receiver<OrganizationProxy>()
{
public void onSuccess(OrganizationProxy response)
{
database.put(requestContext.edit(response)); //fails because a request is already in progress
}
});
I resolved this by using a disposable request context to create the request, and then using my more-permanent request context to edit the object:
temporaryRequestContext.findOrganization(id).fire(new Receiver<OrganizationProxy>()
{
public void onSuccess(OrganizationProxy response)
{
database.put(permanentRequestContext.edit(response)); //succeeds because it has not been fired, even though edit() has been called many times
}
});