This post explains how to manage your GWT server-side services with
Spring and Spring MVC, and to inject Spring beans into them. It is based on the 11th entry in
this GWT forum post.
Next, the DispatcherServlet must choose a org.springframework.web.portlet.mvc.Controller to process the request. It does this by using one of many so-called "handlers" or "handler mappers," which you can specify in the [servlet-name]-servlet.xml file. (In our case (see Figure 1), this file is mywebapp-servlet.xml.) So a DispatcherServlet routes URLs to a handler mapper, and a handler mapper routes URLs to Spring controllers. Figure 2 illustrates how to use the SimpleUrlHandlerMapping handler to route a URL to a controller class GWTController, which we will be writing later on.<web-app>
...
<servlet>
<servlet-name>mywebapp</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>mywebapp</servlet-name>
<url-pattern>*.whatever</url-pattern>
</servlet-mapping>
...
</web-app>
The code in Figures 1 and 2 says that the DispatcherServlet will take any request whose URL ends in "login.whatever" and forward it to a class GWTController whose application context id is loginController. (Figure 2 does not show the body of loginController, where other beans may be injected into it; we will get to that later.) Any URLs that end in "foo.whatever" will also be routed to an instance of GWTController, but it will have different beans injected to it from the ones injected into loginController. (The injections are not shown in Figure 2.)<beans>
<!-- == SPRING DISPATCH HANDLER == -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>
/**/login.whatever=loginController
/**/userProfile.whatever=fooController
</value>
</property>
</bean>
...
<bean name="loginController" class="com.foo.servlet.GWTController">
...
</bean>
<bean name="userProfileController" class="com.foo.servlet.GWTController">
...
</bean>
The server-side code that gets invoked from the client is often referred to as a service, so the act of making a remote procedure call is sometimes referred to as invoking a service. To be clear, though, the term service in this context isn't the same as the more general "web service" concept. In particular, GWT services are not related to the Simple Object Access Protocol (SOAP).Furthermore, every service must implement RemoteService and extend RemoteServiceServlet. RemoteService ties the service to the client-side code that will call it, because they both implement this interface. RemoteServiceServlet extends javax.servlet.http.HttpServlet, and contains all the RPC encoding and decoding code, which every GWT service obviously needs. When you use a GWT service without Spring, the client's call to the service calls the RemoteServiceServlet's doPost() method. (If you look at the GWT source code, you'll see this happens via reflection when the Ajax client call is made.)
Browser: http://xyz/login.whateverNow let's look at processCall(). Without Spring, whenever a GWT client makes an Ajax RPC call to a GWT (RemoteServiceServlet) service, the client is sending an RPC-encoded payload, and the calls to doPost() and processCall() are hidden from you the GWT programmer (they are called by reflection). The service's processCall() will RPC decode the payload with the code shown in Figure 4.
-> DispatcherServlet
-> SimpleUrlHandlerMapping
-> loginController::Controller:handleRequest()
-> loginController::RemoteServletService:doPost()
-> loginController::RemoteServletService:processCall()
...
public void processCall() {
...
RPCRequest rpcRequest = RPC.decode(payload, this);
...
}
In addition to injecting a single GWT service into GWTController, you can first inject as many non-GWT classes as you wish into the GWT service. These will typically come from your business layer. And that, after all, is probably why you wanted to use Spring in the first place! Figure 6 contains sample code showing how this might be done.import javax.servlet.http.HttpServletRequest;public class GWTController extends RemoteServiceServlet implements Controller {
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;
// Instance fields
private RemoteService remoteService;
private Class remoteServiceClass;
// Public methods
/**
* Call GWT's RemoteService doPost() method and return null.
*
* @param request The current HTTP request
* @param response The 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.
* @param payload The RPC payload
*/
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();
}
}
<beans>That's it! Enjoy using GWT with Spring.
...
<!-- == SPRING DISPATCH HANDLER == -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>
/**/login.whatever=loginController
/**/userProfile.whatever=userProfileController
</value>
</property>
</bean>
...
<!-- == LOGIN GWT SERVICE == -->
<bean name="loginController" class="com.aspentech.imos.servlet.GWTController">
<property name="remoteService">
<bean class="com.foo.gwt.login.server.LoginServiceImpl">
<constructor-arg index="0" ref="someNonGWTService1"/>
<constructor-arg index="0" ref="someNonGWTService2"/>
<constructor-arg index="0" ref="someNonGWTService3"/>
</bean>
</property>
</bean>
<!-- == USER PROFILE GWT SERVICE == -->
<bean name="userProfileController" class="com.aspentech.imos.servlet.GWTController">
<property name="remoteService">
<bean class="com.foo.gwt.login.server.UserProfileService">
<constructor-arg index="0" ref="someNonGWTService10"/>
<constructor-arg index="0" ref="someNonGWTService11"/>
<constructor-arg index="0" ref="someNonGWTService12"/>
</bean>
</property>
</bean>
</beans>
