Monday, September 8, 2008

Using Spring MVC Controllers in Grails

Groovy is slower than Java and sometimes dramatically slower. Realistically, this has little impact on a web application since response time is affected more by the database and network latency, so as long as the slowdown isn't too dramatic, the benefits of Groovy and Grails far outweigh these concerns. And Grails is still way faster than Rails :)

But having said that, I was wondering how to use a regular Java Spring MVC controller and JSP instead of a Grails controller and a GSP (both of which use Groovy). Turns out it's pretty easy:

  • Register the traditional Spring dispatcher servlet in web.xml (you'll need to have run grails install-templates). In this example the name (SpringMVC) isn't important, use whatever you want, and I've chosen to map *.action URLs to this controller and let Grails handle the rest:

<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>

  • Generate web-app/WEB-INF/SpringMVC-servlet.xml:

<?xml version='1.0' encoding='UTF-8'?>

<beans xmlns='http://www.springframework.org/schema/beans'
   xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
   xmlns:p='http://www.springframework.org/schema/p'
   xmlns:lang='http://www.springframework.org/schema/lang'
   xsi:schemaLocation='
      http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
      http://www.springframework.org/schema/lang
      http://www.springframework.org/schema/lang/spring-lang-2.5.xsd'>

<bean id='mvcHandlerMapping'
class='org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping'
      p:order='1'>
<property name='interceptors'>
<list>
<ref bean='openSessionInViewInterceptor' />
<ref bean='localeChangeInterceptor' />
</list>
</property>
</bean>

<bean id='mvcViewResolver'
class='org.springframework.web.servlet.view.UrlBasedViewResolver'
      p:viewClass='org.springframework.web.servlet.view.InternalResourceView'
      p:order='1'
      p:prefix='/WEB-INF/jsp/'
      p:suffix='.jsp'
/>

<bean name='baseSimpleController' abstract='true' p:cacheSeconds='0'/>

<bean name='jspController'
class='com.foo.spring.controller.JspController'
parent='baseSimpleController'
abstract='true'
/>

<!-- actions -->

<bean name='/test.action'
class='com.foo.spring.controller.TestController'
parent='baseSimpleController'
      p:successView='test'
/>

<bean name='/other.action' parent='jspController' p:successView='other' />

</beans>

And that's it. Some notes:

  • the handler mapping uses the id mvcHandlerMapping since Grails will create one using the standard name of handlerMapping
  • since handler mappings are auto-discovered by default, you need to set the order attribute to something lower than the Grails mapping's (which uses the default value of Integer.MAX_VALUE) so this mapping is accessed first
  • the HandlerInterceptors that are configured for the Grails mapping (OpenSessionInView, LocaleChange) won't be automatically available to this mapping, but it's simple to borrow them since they're registered as beans; you can also add other custom interceptors to the list
  • I've created an optional abstract parent controller bean (baseSimpleController) for simple controllers (single-page, i.e. not form or wizard controllers)
  • I've also created a simple controller that just shows a JSP – this is useful for pages that don't have any controller logic:

    package com.foo.spring.controller;

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.AbstractController;

    public class JspController extends AbstractController {

    private String _successView;

       @Override
    protected ModelAndView handleRequestInternal(
    final HttpServletRequest request,
    final HttpServletResponse response) {

    return new ModelAndView(_successView);
    }

    public void setSuccessView(final String view) {
          _successView = view;
    }
    }

I've mapped two sample URLs – /test.action, which uses a controller, and /other.action, which uses JspController to just show other.jsp.

Note that it is possible to use JSPs with Grails; Grails looks for a GSP using the specified name, but if it doesn't find one it looks for a JSP (under /WEB-INF/grails-app/views/) and uses that if it exists. So another option is to use Grails controllers and JSP.

Big caveat: I haven't used this in production yet – I'm just prototyping so I'll have this available in the future just in case.

original post

No comments: