»
July 15, 2009
»

GWT Commands - Spring Integration

I’ve created and uploaded to bitbucket a small test/sample GWT-Spring application what explores one of easiest ways to integrate GWT Command Pattern (See my previous blog entry) with Spring server back-end.

gwt-spring/screenshot.png

The app demonstrates integration with simple ServerInfoAction with ServerInfoResult and ServerInfoActionHandler:

public class ServerInfoAction implements Action<ServerInfoResult> {

  private boolean includeUserAgent;

  public ServerInfoAction() {
    ;
  }

  public ServerInfoAction(boolean includeUserAgent) {
    this.includeUserAgent = includeUserAgent;
  }

  public boolean isIncludeUserAgent() {
    return includeUserAgent;
  }

}
public class ServerInfoResult implements Result {

  private String serverInfo;
  private String userAgent;

  public String getServerInfo() {
    return serverInfo;
  }

  public String getUserAgent() {
    return userAgent;
  }

  public void setServerInfo(String serverInfo) {
    this.serverInfo = serverInfo;
  }

  public void setUserAgent(String userAgent) {
    this.userAgent = userAgent;
  }

}
public class ServerInfoActionHandler implements ActionHandler<ServerInfoAction,           ↩
    ServerInfoResult> {

  public ServerInfoResult execute(ServerInfoAction action) {
    ServerInfoResult res = new ServerInfoResult();

    res.setServerInfo(ActionDispatchContext.getServletcontext().getServerInfo());

    if (action.isIncludeUserAgent())
      res.setUserAgent(ActionDispatchContext.getRequest().getHeader("User-Agent"));

    return res;
  }

}

I’m not going to describe the command pattern itself. See GWT mailing list and in previous post mentioned Google I/O video for lots of details and better description than I could write. Instead I’ll step thru implementation details of Spring integration.

And let’s start with web.xml:

<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
                      http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>

  <servlet>
    <servlet-name>rpc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:dispatchServletContext.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>rpc</servlet-name>
    <url-pattern>*.rpc</url-pattern>
  </servlet-mapping>

</web-app>

I’m defining standard Spring DispatcherServlet, pointing to *.rpc this will handle GWT command requests. Next step is to handle requests. This is done by using Spring SimpleUrlHandlerMapping by mapping /**/dispatch.rpc to Spring Controller:

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
  <property name="interceptors">
    <list>
      <!--
        <ref bean="openSessionInViewInterceptor" />
        or whatever else per request
      -->
    </list>
  </property>
  <property name="mappings">
    <value>
      /**/dispatch.rpc=actionDispatchTransportService
    </value>
  </property>
</bean>

actionDispatchTransportService is defined as follows:

<bean id="actionDispatchTransportService" class="com.amateurinmotion.dispatch.server.     ↩
  action.support.impl.ActionDispatchTransportServiceImpl">
  <property name="actionDispatcher">
    <bean 
      class="com.amateurinmotion.dispatch.server.action.support.impl.ActionDispatcherImpl">
      <property name="actionHandlerRegistry" ref="actionHandlerRegistry" />
    </bean>
  </property>
</bean>  

The ActionDispatchTransportServiceImpl by itself is RemoteServiceServlet and Controller:

@SuppressWarnings("serial")
public class ActionDispatchTransportServiceImpl extends RemoteServiceServlet implementsActionDispatchTransportService, Controller, ServletContextAware {

  private ServletContext servletContext;
  private ActionDispatcher actionDispatcher;

  @Required
  public void setActionDispatcher(ActionDispatcher actionDispatcher) {
    this.actionDispatcher = actionDispatcher;
  }

  // ActionDispatchTransportService

  public Result execute(Action<?> action) throws Exception {
    return actionDispatcher.execute(action);
  }

  // Controller

  public ModelAndView handleRequest(HttpServletRequest request,                           ↩
      HttpServletResponse response) throws Exception {
    ActionDispatchContext.set(request, response, getServletContext());
    try {
      super.doPost(request, response);
    } finally {
      ActionDispatchContext.remove();
    }
    return null;
  }

  // ServletContextAware

  public void setServletContext(ServletContext servletContext) {
    this.servletContext = servletContext;
  }

  @Override
  public ServletContext getServletContext() {
    return servletContext;
  }

}

When Controller receives request the handleRequest method is called, it is delegated to GWT doPost what after deserialization calls execute what delegates request to actionDispatcher who using actionHandlerRegistry lookups ActionHandler for given Action and invokes it.

ActionHandler definition list looks like this:

<bean id="actionHandlerRegistry" class="com.amateurinmotion.dispatch.server.action.       ↩
    support.impl.ActionHandlerRegistryImpl">
  <property name="handlers">
    <list>
      <bean class="com.amateurinmotion.dispatch.server.action.ServerInfoActionHandler" />
    </list>
  </property>
</bean>

For full details see http://bitbucket.org/ampatspell/dispatch (To run locally you’ll need Eclipse and few tweaks in .classpath)

Note: This is not production ready code

 
Internet Explorer 6
Are you serious?