Tuesday, February 9, 2010

Google Web Kit, Spring and Serialization

I've just started playing with Google's Web Toolkit and I am impressed. Even a back-end programmer like me can write rich, AJAX front-ends!

However, integrating it with Spring is not trivial. There are many suggestions on how to do this. The simplest appeared to be Richard Bondi's suggestion that you can find here. It worked fine. Until, that is, I tried to send one of my own objects from client to server. I started seeing this:


Exception while dispatching incoming RPC call
com.google.gwt.user.client.rpc.SerializationException: Type 'xxx.xx.xxx.MyClass'
was not assignable to 'com.google.gwt.user.client.rpc.IsSerializable'
and did not have a custom field serializer.
For security purposes, this type will not be serialized.



And, after stepping through the code (thank you, Google, for providing the source code by default with my Eclipse plug-in!) I saw that it was using the LegacySerializationPolicy that dates back to GWT 1.3, apparently.

That's no good! So, why wasn't Richard's excellent fix fully functional?

After a late night hacking session, I got a rough solution. I modified Richard's GWTController.processCall's try block such that it read:

SerializationPolicyProvider serializationPolicyProvider = new SerializationPolicyProvider() {
@Override
public SerializationPolicy getSerializationPolicy(
String moduleBaseURL,
String serializationPolicyStrongName) {
remoteService.setPerThreadRequest(perThreadRequest);
return remoteService.getSerializationPolicy(moduleBaseURL, serializationPolicyStrongName);
}
};
RPCRequest rpcRequest =
RPC.decodeRequest(payload, this.remoteServiceClass, serializationPolicyProvider);


Where your remoteService now has to inherit from this ugly class:

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

import org.springframework.web.context.ServletContextAware;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

public class MyRemoteServiceServlet extends RemoteServiceServlet implements ServletContextAware {

private ServletContext servletContext;

public void setPerThreadRequest(ThreadLocal perThreadRequest) {
this.perThreadRequest = perThreadRequest;
}

@Override
public ServletContext getServletContext() {
return servletContext;
}

public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}

}


OK, it's not pretty but it works. And you had to extend RemoteServiceServlet anyway.

There may be a more elegant way but I've only just woke up so I will continue improving it and post any further thoughts later.

3 comments:

  1. Simplier solution:

    RPCRequest rpcRequest = RPC.decodeRequest(payload, this.remoteServiceClass, this);

    ReplyDelete
  2. @Pavel: Thanks, Pavel, but with this code you suggest you get:

    java.lang.NullPointerException
    at com.google.gwt.user.server.rpc.RemoteServiceServlet.loadSerializationPolicy(RemoteServiceServlet.java:49)
    at com.google.gwt.user.server.rpc.RemoteServiceServlet.doGetSerializationPolicy(RemoteServiceServlet.java:251)
    .
    .
    .

    I believe this is because the GWT Servlet no longer has access to the Request object. That's been taken care of by Spring.

    Or is there some cunning fix for this that you know of?

    Thanks,

    Phill

    ReplyDelete
  3. remoteService.setPerThreadRequest(perThreadRequest);
    return remoteService.getSerializationPolicy(moduleBaseURL, serializationPolicyStrongName);
    }

    I got a error from your codes:
    The method setPerThreadRequest(ThreadLocal) is undefined for the type RemoteService


    how did you fix it?

    my email is jason.jia@rbcdexia.com, please.

    Thanks

    ReplyDelete