Saturday, January 1, 2011

GWT RPC with Phonegap

Update: This is no longer necessary. Take a look at a newer post!


I have been asked a couple of times now how to use gwt rpc with phonegap applications. Right now this does not work out of the box for a simple reason:

For security reasons phonegap starts your application locally (not http:// but file://pathToYourApp/www/index.html). GWT on the other hand assumes that it will find the services by appending your annotated servlet path to the base url. So GWT phonegap applications out of the box try to find their services locally.

In order to fix this you have to do a couple of things (which will be included in the next version of gwt-phonegap)

You have to set the url of your servlet in the proxy service interface like so:

DispatchServiceAsync realService = GWT.create(DispatchService.class);
ServiceDefTarget service = (ServiceDefTarget) realService;
service.setServiceEntryPoint("http://www.daniel-kurka.de/yourservice");

Normally I include a switch for production and dev time.


The other thing that is not working out of the box is the RemoteServiceServlet. I looks for the gwt.rpc files at the url that the client tells it (which would be file:// something). So serialization of some object may fail because its working in gwt 1.3 mode.

To fix this you can just look locally for those files by overriding doGetSerializationPolicy like this:


@Singleton
public class PhoneGapDispatchServiceServlet extends RemoteServiceServlet implements DispatchService {

 private static final long serialVersionUID = 9114953736786309471L;
 private final Dispatch dispatch;

 private Logger logger = Logger.getLogger(getClass().getName());

 @Inject
 public PhoneGapDispatchServiceServlet(Dispatch dispatch) {
  this.dispatch = dispatch;
 }

 public Result execute(Action action) throws ActionException {
  try {
   return dispatch.execute(action);
  } catch (RuntimeException e) {
   logger.log(Level.SEVERE, "Exception while executing " + action.getClass().getName(), e);
   throw new ActionException("runtime exception occured while dispatching action");
  }
 }

 private String getBaseUrl(HttpServletRequest request) {
  if (request.getServerPort() == 80 || request.getServerPort() == 443)
   return request.getScheme() + "://" + "127.0.0.1" + request.getContextPath() + "/iphone/";
  else
   return request.getScheme() + "://" + "127.0.0.1" + ":" + request.getServerPort() + request.getContextPath() + "/iphone/";

 }

 @Override
 protected SerializationPolicy doGetSerializationPolicy(HttpServletRequest request, String moduleBaseURL, String strongName) {

  String baseUrl = getBaseUrl(request);
  logger.info("localUrl: " + baseUrl);

  return super.doGetSerializationPolicy(request, baseUrl, strongName);
 }

}


This is all you have to do to get GWT RPC working with phonegap apps.

2 comments:

  1. Seems very nice, but does it work into App Engine? I got some errors I was unable to fix! ( com.sun.net.httpserver.Authenticator.Result is not supported by Google App Engine's Java runtime environment ).
    Or an import problem?

    ReplyDelete
  2. Is this still the best way to get GWT RPC working? Have you made any updates in recent versions of get-phonegap to improve this process?
    I thought that the whole thing was an issue with the browser not allowing you to hit a remote URL:
    // Note that the target URL must reside on the same domain and port from
    // which the host page was served.

    ReplyDelete