Matt Raible has been building web applications for most of his adult life. He started tinkering with the web before Netscape 1.0 was even released. For the last 16 years, Matt has helped companies adopt open source technologies (Spring, Hibernate, Apache, Struts, Tapestry, Grails) and use them effectively. Matt has been a speaker at many conferences worldwide, including Devoxx, Jfokus, ÜberConf, No Fluff Just Stuff, and a host of others.

Matt is a DZone MVB and is not an employee of DZone and has posted 143 posts at DZone. You can read more from them at their website. View Full User Profile

How to do Cross-Domain GWT RPC With a ProxyServlet

08.06.2009
| 10941 views |
  • submit to reddit

Last week, I started working on a new project using GWT. On my last project, we used GWT HTTP Calls and my new project is using RPC. We'll likely migrate to a JSON backend eventually, but in the meantime, I wanted to be able to develop in hosted mode (localhost:8888) and call services on another host (localhost:8080), where the services are running in a JSF/Spring webapp.

At first, I thought it'd be easy thanks to the handy-dandy ProxyServlet I mentioned in Implementing OAuth with GWT. However, when I tried to hook it in and use it, I saw the following error in my server-side logs.

java.lang.NullPointerException
at javax.servlet.GenericServlet.getServletName(GenericServlet.java:322)
at javax.servlet.GenericServlet.log(GenericServlet.java:277)
at com.google.gwt.user.server.rpc.RemoteServiceServlet.doGetSerializationPolicy(RemoteServiceServlet.java:219)
at com.google.gwt.user.server.rpc.RemoteServiceServlet.getSerializationPolicy(RemoteServiceServlet.java:117)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.prepareToRead(ServerSerializationStreamReader.java:429)
at com.google.gwt.user.server.rpc.RPC.decodeRequest(RPC.java:234)

Looking at RemoteServiceServlet.java:219, there's a logging call that fails for some reason (at least in my application).

/*
* Check that the module path must be in the same web app as the servlet
* itself. If you need to implement a scheme different than this, override
* this method.
*/
if (modulePath == null || !modulePath.startsWith(contextPath)) {
String message = "ERROR: The module path requested, "
+ modulePath
+ ", is not in the same web application as this servlet, "
+ contextPath
+ ". Your module may not be properly configured or your client and server code maybe out of date.";
log(message, null);
}

In the above code, you might notice that GWT is checking to make sure the client is hosted in the same application as the server. After I figured this out, it was pretty easy to modify my ProxyServlet to trick GWT RPC into thinking the client was in the same web application. In the ProxyServlet's handleContentPost method, I added the following code to replace "localhost:8888/" with "localhost:8080/services/" (in the content of the post to the server). 

if (contentType.startsWith("text/x-gwt-rpc")) {
String clientHost = httpServletRequest.getLocalName();
if (clientHost.equals("127.0.0.1")) {
clientHost = "localhost";
}

int clientPort = httpServletRequest.getLocalPort();
String clientUrl = clientHost + ((clientPort != 80) ? ":" +
clientPort : "");
String serverUrl = stringProxyHost + ((intProxyPort != 80) ? ":" +
intProxyPort : "") + httpServletRequest.getServletPath();
postContent = postContent.replace(clientUrl , serverUrl);
}

After manipulating the posted content, I was successfully able to use GWT RPC cross-domain.

Woo hoo!

For your convenience, the full handleContentPost() method is listed below.

private void handleContentPost(PostMethod postMethodProxyRequest, 
HttpServletRequest httpServletRequest)
throws IOException, ServletException {
StringBuilder content = new StringBuilder();
BufferedReader reader = httpServletRequest.getReader();
for (;;) {
String line = reader.readLine();
if (line == null) break;
content.append(line);
}

String contentType = httpServletRequest.getContentType();
String postContent = content.toString();

if (contentType.startsWith("text/x-gwt-rpc")) {
String clientHost = httpServletRequest.getLocalName();
if (clientHost.equals("127.0.0.1")) {
clientHost = "localhost";
}

int clientPort = httpServletRequest.getLocalPort();
String clientUrl = clientHost + ((clientPort != 80) ? ":" +
clientPort : "");
String serverUrl = stringProxyHost + ((intProxyPort != 80) ? ":" +
intProxyPort : "") + httpServletRequest.getServletPath();
postContent = postContent.replace(clientUrl , serverUrl);
}

String encoding = httpServletRequest.getCharacterEncoding();
debug("POST Content Type: " + contentType + " Encoding: " + encoding,
"Content: " + postContent);
StringRequestEntity entity;
try {
entity = new StringRequestEntity(postContent, contentType, encoding);
} catch (UnsupportedEncodingException e) {
throw new ServletException(e);
}
// Set the proxy request POST data
postMethodProxyRequest.setRequestEntity(entity);
}

From http://raibledesigns.com/rd/entry/how_to_do_cross_domain 

Published at DZone with permission of Matt Raible, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Ganesh Bansal replied on Fri, 2009/08/28 - 8:53am

Hello Matt

I also need to make cross domain AJAX calls in my GWT application. I read your article but could not understand what changes, I need to do to make it possible. Could you please post some more details or send some articles to me at ganesh.bansal@gmail.com in this regard. Kindly excuse me if I am asking very basic question.

 

Thanks in advance

Ganesh

Matt Raible replied on Fri, 2009/08/28 - 10:28am in response to: Ganesh Bansal

Ganesh - I updated the original post with ProxyServlet code and configuration instructions. Hope this helps.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.