My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
IntegratingWithSpring  
How to integrate GWT with Spring 2.0
Type-FAQ
Updated Feb 4, 2010 by ajr+personal@google.com

Explained: How to Integrate GWT with Spring 2.0

original by Richard Bondi

These instructions are based on this GWT newsgroup post.

Prerequisites: You are comfortable with using Spring without GWT. You also know the basics of how to code a GWT client making an RPC call to a GWT service. Specifically, you know that for any GWT module:

  • (module)/client/MyService.java interface extends GWT's RemoteService interface;
  • (module)/server/MyServiceImpl.java ...
    • ...implements RemoteService too;
    • ...extends GWT's RemoteServiceServlet, which extends javax.servlet.http.HttpServlet.

Spring's MVC works first by your browser request/url being redirected to DispatcherServlet.

So let's make that happen. First, we'll configure our web.xml file to redirect all urls ending in "whatever" to go to Spring's DispatcherServlet:

File: web.xml
<web-app>
...
<servlet-name>gwtGumby</servlet-name>
  <servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>

<servlet-mapping>
  <servlet-name>gwtGumby</servlet-name>
  <url-pattern>*.whatever</url-pattern>
</servlet-mapping>
...
</web-app>

So far so good. Next, the DispatcherServlet will have to choose a org.springframework.web.portlet.mvc.Controller to process this request. But how does it choose?

With either a BeanNameUrlHandlerMapping or a SimpleUrlHandlerMapping implementation, which you specify in an application context file. Here's an example:

File: applicationContext.xml (or related file) (ignore the syntax highlighting, it's wrong)
<beans>

<!-- == SPRING DISPATCH HANDLER == -->

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
  <value>
    /**/chart.whatever=chartController
    /**/gumby.whatever=gumbyController
  </value>
</property>
</bean>
...

This says that the DispatcherServlet will take any request whose url ends in "chart.whatever" and forward it to a Spring org.springframework.web.portlet.mvc.Controller whose application context id is "chartController".

I'll show you the application context entries for chartController and gumbyController only at the end of this post. Before that, we have to look at these controllers.

Or rather, Controller: it's actually just one Controller, which will be called with different injections for different requests.

The code of this Controller class, which we've named GWTController, is shown below; here is how it works.

How GWTController works

GWTController extends GWT's RemoteServiceServlet, but it also implements Spring's MVC Controller interface. When DispatcherServlet calls this Controller, it calls its handleRequest() method; handleRequest() simply calls doPost(), and returns null.

What does doPost() do? Well, it is a GWT RemoteServiceServlet method that overrides javax.servlet.http.HttpServlet#doPost(). Whatever else this GWT method does, it also calls the GWT RemoteServiceServlet's processCall() method.

When a GWT client makes an ajax RPC call to a GWT (RemoteServiceServlet) service, the client is sending an RPC-encoded payload. Under normal circumstances -- in other words, outside of Spring -- the service's processCall() will RPC decode the payload by calling RPCRequest rpcRequest = RPC.decode(payload, this). But that won't work now: this is the Spring Controller we've written, GWTController. Given how we have set things up in Spring, every different GWT client calll will made to the same GWT service, GWTController, instead of every client calling its matching GWT service. What to do?

Answer: have Spring inject the GWT RemoteService so that we can access it from this.remoteServiceClass, and then call RPCRequest rpcRequest = RPC.decodeRequest(payload, this.remoteServiceClass). Voila: now every GWT client will have its payload decoded by the client's corresponding GWT service.

Once again, so far so good. With this neat trick we've captured what some GWT Javascript client sent to us via RPC: we have our decoded payload. Now our GWT (RemoteService) service needs to process it, too. But how?

We just run the same code GWT would run outside of Spring, but once again substituting the injected this.remoteServie for this. We just call:

return RPC.invokeAndEncodeResponse(this.remoteService, rpcRequest.getMethod(), rpcRequest.getParameters());

And now we're done. Everything else in this class is just commentary. See for yourself:

File: GWTController.java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.google.gwt.user.server.rpc.RPCRequest;
import com.google.gwt.user.server.rpc.RPC;
import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.SerializationException;
import org.springframework.web.servlet.mvc.Controller;
import org.springframework.web.servlet.ModelAndView;

import com.google.gwt.user.client.rpc.RemoteService;

public class GWTController
extends RemoteServiceServlet
implements Controller
{
  // Instance fields
  private RemoteService remoteService;
  private Class remoteServiceClass;


  // Public methods
  /**
  * Implements Spring Controller interface method.
  *
  * Call GWT's RemoteService doPost() method and return null.
  *
  * @param request  current HTTP request
  * @param response current HTTP response
  * @return a ModelAndView to render, or null if handled directly
  * @throws Exception in case of errors
  */
  public ModelAndView handleRequest(HttpServletRequest request,
                                 HttpServletResponse response)
                                 throws Exception
  {
    doPost(request, response);
    return null; // response handled by GWT RPC over XmlHttpRequest
  }

  /**
  * Process the RPC request encoded into the payload string and return a string
  * that encodes either the method return or an exception thrown by it.
  */
  public String processCall(String payload) throws SerializationException
  {
    try
    {
       RPCRequest rpcRequest =
           RPC.decodeRequest(payload, this.remoteServiceClass);

       // delegate work to the spring injected service
       return RPC.invokeAndEncodeResponse(this.remoteService,
                                          rpcRequest.getMethod(),
                                          rpcRequest.getParameters() );
    }
    catch (IncompatibleRemoteServiceException e)
    {
      return RPC.encodeResponseForFailure(null, e);
    }
  }

  /**
  * Setter for Spring injection of the GWT RemoteService object.
  * @param RemoteService the GWT RemoteService implementation
  * that will be delegated to by
  * the {@code GWTController}.
  */
  public void setRemoteService( RemoteService remoteService )
  {
    this.remoteService = remoteService;
    this.remoteServiceClass = this.remoteService.getClass();
  }
}

One thing to note is that in addition to injecting a single GWT RemoteService implementation, you can inject as many non-GWT classes, services, etc etc as you wish.

Even better, if you write your GWT RemoteService correctly, you can test it with stubs for all these injections -- in hosted mode, without every having to launch your web application or database! But how to do that is another post.

Finally, then, here is how you inject your GWT service into the GWTController, in an application context file:

File: applicationContext.xml (or related file)
<beans>

  <!-- == SPRING DISPATCH HANDLER == -->

  <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
      <value>
      /**/chart.whatever=chartController
      /**/gumby.whatever=gumbyController
      </value>
    </property>
  </bean>
  ...

  <!-- == CHART GWT SERVICE == -->

  <bean name="chartController" class="com.aspentech.imos.servlet.GWTController">
    <property name="remoteService">
      <bean class="com.foo.gwt.chart.server.ChartServiceImpl"/>
    </property>
  </bean>

  <!-- == GUMBY GWT SERVICE == -->

  <bean name="gumbyController" class="com.aspentech.imos.servlet.GWTController">
    <property name="remoteService">
      <bean class="com.foo.gwt.gumby.server.GumbyServiceImpl">
        <constructor-arg index="0" ref="someNonGWTService1"/>
        <constructor-arg index="0" ref="someNonGWTService2"/>
        <constructor-arg index="0" ref="someNonGWTService3"/>
      </bean>
    </property>
  </bean>

</beans>

That's it! Enjoy using GWT with Spring.

Comment by p...@pgt.de, Oct 5, 2007

Sounds good, except for the part where I should edit the web.xml. It is generated, and thus not editable. What is best setup here?

Comment by dgreen...@gmail.com, Jan 8, 2008

The simplest way =)

import javax.servlet.ServletContext;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;



import org.springframework.web.context.ServletContextAware;

import org.springframework.web.servlet.ModelAndView;

import org.springframework.web.servlet.mvc.Controller;



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



public class GWTController extends RemoteServiceServlet implements Controller,

		ServletContextAware {

	private ServletContext servletContext;



	public ModelAndView handleRequest(HttpServletRequest request,

			HttpServletResponse response) throws Exception {

		super.doPost(request, response);

		return null;



	}



	public ServletContext getServletContext() {

		return servletContext;

	}



	public void setServletContext(ServletContext servletContext) {

		this.servletContext = servletContext;

	}



}
Comment by p...@pgt.de, Feb 27, 2008

I have described how I integrated Spring without loosing "out of the box GWT works in the IDE with the Shell" in my blog.

Could be a good start for beginners...

http://adminsight.de/2008/02/14/non-invasive-gwt-and-spring-integration/

Comment by rbo...@gmail.com, Apr 27, 2008

The link to the author is wrong, it should be gwttips.blogspot.com (not gwttips.blogpspot.com).

Comment by razi.alq...@gmail.com, Sep 18, 2008

Dear all how can i configure the web.xml to load the spring context (DispatcherServlet?) in the host mode note that every time the GWT Shell create a new web.xml file without taking in consideration my xml, i need to use the host mode .. please help..

Comment by pgtabo...@gmail.com, Feb 23, 2009

I found no way to edit my previous content. The link to my blog posting is:

http://pgt.de/2008/02/14/non-invasive-gwt-and-spring-integration/

Comment by hakan.er...@gmail.com, Mar 5, 2009

to send objects over rpc which implement java.io.Serializable instead of IsSerializable? replace the return statement with the code below:

return RPC.invokeAndEncodeResponse(this.remoteService, rpcRequest.getMethod(), rpcRequest.getParameters(), rpcRequest.getSerializationPolicy());

hope this helped before you started hitting your head to walls ;) r a f t

Comment by pgtabo...@gmail.com, Sep 18, 2009

Before you start wiring up beans as Gwt-RPC endpoints or start creating lots of RPC services, have a look at the command pattern approach described Ray Ryan at his talk. At the end, all you need is one single service, without loosing type safety.

A wrap up with link to the talk (video) and to the projects that either already where there b4 Ryans talk or were created upon the talk can be found here:

http://pgt.de/2009/09/18/best-practices-for-architecting-your-gwt-app/

There you will find a very simple and concise way to use Spring in the backend with gwt-dispatch.

Have fun!

Comment by juri.strumpflohner, Sep 27, 2009

I'm getting a NullPointerException? when calling the getServletContext(). Anyone has a clue why this happens??

Comment by juri.strumpflohner, Oct 5, 2009

Fixed my NullPointerException??. The above GWTController code should maybe be changed like I described in this post

http://blog.js-development.com/2009/09/gwt-meets-spring.html

I guess more people are having this issue.

Juri

Comment by PhillHe...@gmail.com, Feb 9, 2010

Hi!

Great fix! I was happily using it until I tried to send objects of my own type from client to server when it broke with SerializationException?.

I've got an addition to your fix at:

http://javaagile.blogspot.com/2010/02/ive-just-started-playing-with-googles.html

Thanks once again!

Phill

Comment by tehdan...@gmail.com, Feb 9, 2010

Why is the fanciness needed? I got it working using Spring MVC annotations and a basic base class for the GWT services. Maybe it's "tied into" Spring MVC too much for your liking?

The base class for GWT services:

public abstract class BaseRemoteService extends RemoteServiceServlet implements ServletContextAware {
	
	private ServletContext servletContext;
	
	@RequestMapping(method = { RequestMethod.GET, RequestMethod.POST })
	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
		doPost(request, response);
		return null; // response handled by GWT RPC over XmlHttpRequest
	}

	@Override
	public void setServletContext(ServletContext servletContext) {
		this.servletContext = servletContext;
	}
	
	@Override
	public ServletContext getServletContext() {
		return this.servletContext;
	}
}

An example class autowired and mapped using annotations:

@Controller
@RequestMapping("/**/thing.service")
public class ThingService extends BaseRemoteService implements IThingService {
	@Autowired
	private ISystemService systemService;
	
	@Override
	public String getVersion() {
		return systemService.getVersion();
	}
}

And the dispatcher XML contains only the lines:

	<context:component-scan base-package="au.gov.vic.dpi.contactmanager.gwt.server">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

The ISystemService is defined in another XML autowiring by name (it's a remote service) that the ContextLoaderListener? is initialising.

Comment by tehdan...@gmail.com, Feb 9, 2010

The above is pretty much what dgreen.ru said, but using annotations instead of the Controller interface.

Comment by sreejipnr@gmail.com, Feb 17, 2010

Hi,

Can any one tell me, how do I get the http session in the implementation class as we are not directly implementing RemoteServiceServlet?.

Sree-

Comment by rajesh.k...@gmail.com, Feb 24, 2010

The HttpServletRequest? object in the remote service seems to be null. Any one has clue?

Comment by yog...@gmail.com, Apr 18, 2010

Did any one get this exception ???

SEVERE: Exception while dispatching incoming RPC call 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) at com.test.server.generic.controller.GWTRPCController.processCall(GWTRPCController.java:94)

Comment by kirtigup...@gmail.com, May 7, 2010

Hi ! A piece of code in my server side impl class invokes getThreadLocalRequest() method, which works fine when GWTServletImpl is directly used as a servlet in web.xml.

But when I used GWTController for spring integration, work is delegated to server side impl class using statement:

RPC.invokeAndEncodeResponse(this.remoteService, rpcRequest.getMethod(), rpcRequest.getParameters(), rpcRequest.getSerializationPolicy());

And this.getThreadLocalRequest() invocation in my actual impl class returns null.

How to handle this ? Help !!

Comment by makhlouf...@gmail.com, Jun 2, 2010
kirtigupta06 did you find any solution for the null point on this.getThreadLocalRequest() ??

Any help please????

Comment by cili...@gmail.com, Aug 31, 2010

Hi yogu13, I don't know if can be useful for you now, but I have the same problem. Is because the config object is null inside GenericServlet?. My solution is to implement the interface ServletConfigAware? in GWTController. then put this method

@Override

public void setServletConfig(ServletConfig? servletConfig) {
try {
super.init(servletConfig);
} catch (ServletException? e) {
e.printStackTrace();
}
}

I hope somebody can find this useful

Comment by leo.sh...@gmail.com, Dec 12, 2010

@kirtigupta06, @makhloufelyes

did you find any solution for the null point on this.getThreadLocalRequest() ??

Any help please????

Comment by nava.a...@gmail.com, Apr 15, 2011

Hi, my GWTController invokes my RemoteService? which has been injected by Spring, But finally it throws following error..

java.lang.NullPointerException?

at javax.servlet.GenericServlet?.getServletContext(GenericServlet?.java:160) at com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet?.doUnexpectedFailure(AbstractRemoteServiceServlet?.java:109) at com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet?.doPost(AbstractRemoteServiceServlet?.java:67) at com.mycompany.server.GWTController.handleRequest(GWTController.java:43)

Can anyone found any solution for this............

Comment by j.singh....@gmail.com, Apr 20, 2011

Thanks for posting this code. I'm using GWT RequestFactory? instead of RPC. I have not yet found any good resource on integration of RF + Spring 3.0 or even 2.5+. I have tried myself but unsuccessful. Do you guys know of a resource that talks about this? Any help is much appreciated.

Cheers :)

Comment by josgr...@gmail.com, May 19, 2011

@EasierWay if you use the code above with GWT2 you might have some issues with serialization because the RemoteServiceServlet changed. Easier way is to check if the member is not null and invoke it, otherwise delegate the call to the superclass. When I attempted using the code "as-is" from the article I was getting various issues because the signatures and method body changed in GWT2.

@SuppressWarnings("serial")
public class SpringControllerGWT extends RemoteServiceServlet
		implements Controller{
	protected RemoteService remoteService;
	protected ServletContext servletContext;


	/**
	 * Implements Spring Controller interface method.
	 * 
	 * Call GWT's RemoteService doPost() method and return null.
	 * 
	 * @param request
	 *            current HTTP request
	 * @param response
	 *            current HTTP response
	 * @return a ModelAndView to render, or null if handled directly
	 * @throws Exception
	 *             in case of errors
	 */
	public ModelAndView handleRequest(HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		if(getRemoteService()!=null){
			Controller controller = (Controller)getRemoteService();
			return controller.handleRequest(request, response);
		}
		doPost(request, response);
		return null; // response handled by GWT RPC over XmlHttpRequest
	}

	/**
	 * Process the RPC request encoded into the payload string and return a
	 * string that encodes either the method return or an exception thrown by
	 * it.
	 */
	@Override
	public String processCall(String payload) throws SerializationException {
		if(getRemoteService()!=null){
			RemoteServiceServlet remoteServlet = (RemoteServiceServlet)getRemoteService();
			return remoteServlet.processCall(payload);
		}
		return super.processCall(payload);
	}

	public Class<?> getRemoteServiceClass() {
		Class<?> retval = null;
		if (getRemoteService() == null) {
			return retval;
		}
		retval = getRemoteService().getClass();
		return retval;
	}

	/**
	 * @return the remoteService
	 */
	public RemoteService getRemoteService() {
		return remoteService;
	}

	/**
	 * @param remoteService
	 *            the remoteService to set
	 */
	public void setRemoteService(RemoteService remoteService) {
		this.remoteService = remoteService;
	}
}
Comment by Artful.T...@gmail.com, Nov 17, 2011

Hi. I'm trying to make a flexible solution to this problem. - resolve service by URL; - resolve service by Annotation;

http://code.google.com/p/gwt-delegate/

http://code.google.com/p/gwt-delegate/source/browse/gwt-delegate-parent/gwt-delegate/src/main/java/org/sgtools/gwt/delegate


Sign in to add a comment
Powered by Google Project Hosting