|
IntegratingWithSpring
How to integrate GWT with Spring 2.0
Type-FAQ Explained: How to Integrate GWT with Spring 2.0original 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:
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)
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 worksGWTController 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
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)
That's it! Enjoy using GWT with Spring. |
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?
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; } }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/
The link to the author is wrong, it should be gwttips.blogspot.com (not gwttips.blogpspot.com).
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..
IMHO a more cleaner way: http://blog.digitalascent.com/2007/11/gwt-rpc-with-spring-2x_12.html
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/
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
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!
I'm getting a NullPointerException? when calling the getServletContext(). Anyone has a clue why this happens??
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
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
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.
The above is pretty much what dgreen.ru said, but using annotations instead of the Controller interface.
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-
The HttpServletRequest? object in the remote service seems to be null. Any one has clue?
Did any one get this exception ???
SEVERE: Exception while dispatching incoming RPC call java.lang.NullPointerException?
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 !!
Any help please????
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
I hope somebody can find this useful
@kirtigupta06, @makhloufelyes
Any help please????
Hi, my GWTController invokes my RemoteService? which has been injected by Spring, But finally it throws following error..
java.lang.NullPointerException?
Can anyone found any solution for this............
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 :)
@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; } }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