September 16th, 2009

Use Spring with GWT dispatch

From the GWT dispatch site:

Inspired by Ray Ryan’s Best Practices For Architecting Your GWT App session at Google I/O 2009, “gwt dispatch” is an implementation of the ‘command pattern’ discussed at the beginning of the video.

The project uses Gin in the frontend and Guice in the backend. Guice defines a very nice dependency injection framework, and as such, competes partially with the Springframework. But the Springframework is by far more than dependency injection, and I don’t want to miss any of its features in the backend.

So I took a look at the gwt dispatch sources, and decided to use Spring to setup the server side of the dispatch service. GWT dispatch extensively uses constructor injection. This works perfectly with Spring, but not with plain servlets. So I needed to rewrite the DispatchServiceServlet a little…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class StandardDispatchServiceServlet extends RemoteServiceServlet
		implements DispatchService {
 
	private Dispatch dispatch;
 
	@Override
	public void init(ServletConfig config) throws ServletException {
		super.init(config);
		WebApplicationContext ctx = WebApplicationContextUtils
				.getRequiredWebApplicationContext(config.getServletContext());
		AutowireCapableBeanFactory beanFactory = ctx
				.getAutowireCapableBeanFactory();
		beanFactory.autowireBean(this);
	}
 
	public Result execute(Action<?> action) throws ActionException {
		try {
			return dispatch.execute(action);
		} catch (RuntimeException e) {
			log("Exception while executing " + action.getClass().getName()
					+ ": " + e.getMessage(), e);
			throw e;
		}
	}
 
	@Autowired
	@Required
	public void setDispatch(Dispatch dispatch) {
		this.dispatch = dispatch;
	}
}

Instead of using constructor injection, I use setter injection. This way the servlet can be simply added to the web.xml. The servlet lifecycle initializes servlets before they are service-ready. I use the initialization callback method to autowire the servlet instance.

Here is my web.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<web-app>
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/spring-conf.xml</param-value>
	</context-param>
	<context-param>
		<param-name>log4jConfigLocation</param-name>
		<param-value>/WEB-INF/log4j.xml</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
	</listener>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
 
 
	<!-- Servlets -->
	<servlet>
		<servlet-name>dispatchServlet</servlet-name>
		<servlet-class>x.y.gwt.hellospringdispatch.server.support.StandardDispatchServiceServlet</servlet-class>
	</servlet>
 
	<servlet-mapping>
		<servlet-name>dispatchServlet</servlet-name>
		<url-pattern>/hellospringdispatch/dispatch</url-pattern>
	</servlet-mapping>
 
	<!-- Default page to serve -->
	<welcome-file-list>
		<welcome-file>HelloSpringDispatch.html</welcome-file>
	</welcome-file-list>
 
</web-app>

I am using annotation based configuration in my project, so the spring configuration file is very concise:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
	     http://www.springframework.org/schema/beans     http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
         http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-2.5.xsd
    "
	>
 
	<context:annotation-config />
	<context:component-scan base-package="x.y.gwt.hellospringdispatch.server" />
 
</beans>

The component scan will add any properly annotated class to the spring application context. Obviously, the classes in the GWT dispatch project are not properly annotated. So I created the classes in my projects that are needed by the DispatchService implementation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import net.customware.gwt.dispatch.server.ActionHandlerRegistry;
import net.customware.gwt.dispatch.server.DefaultDispatch;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
@Component
public class DispatchBean extends DefaultDispatch {
 
	@Autowired
	public DispatchBean(ActionHandlerRegistry handlerRegistry) {
		super(handlerRegistry);
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.util.List;
 
import net.customware.gwt.dispatch.server.ActionHandler;
import net.customware.gwt.dispatch.server.DefaultActionHandlerRegistry;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
@Component
public class ActionHandlerRegistryBean extends DefaultActionHandlerRegistry {
 
	@Autowired
	public void setHandlers(List<ActionHandler<?, ?>> handlers) {
		for (ActionHandler<?, ?> actionHandler : handlers) {
			addHandler(actionHandler);
		}
	}
}

The action handler registry is the place where the ActionHandlers are kept. I used the collection autowire feature to get a list of all handlers registered in the Spring application context.

The last step is to add ActionHandlers to the Spring context. For obvious reasons I prefer to keep my action handlers “Spring free” – that’s why I created my own annotation to identify action handlers:

1
2
3
4
5
6
7
8
9
10
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
 
import org.springframework.stereotype.Component;
 
@Component
@Retention(RetentionPolicy.RUNTIME)
public @interface ActionHandlerBean {
 
}

I am not sure if I need to setup the retention policy – I should have a look at that…

All I need to do now is to add handlers to any package below the server package. The Spring component scan will automatically find the handlers and add them to the Spring application context and further inject them to the handler registry.

Here is a simple ActionHandler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import net.customware.gwt.dispatch.server.ActionHandler;
import net.customware.gwt.dispatch.server.ExecutionContext;
import net.customware.gwt.dispatch.shared.ActionException;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
 
import x.y.gwt.hellospringdispatch.server.support.ActionHandlerBean;
import x.y.gwt.hellospringdispatch.shared.SendGreeting;
import x.y.gwt.hellospringdispatch.shared.SendGreetingResult;
 
@ActionHandlerBean
public class SendGreetingHandler implements
		ActionHandler<SendGreeting, SendGreetingResult> {
 
	public SendGreetingHandler() {
	}
 
	public SendGreetingResult execute(final SendGreeting action,
			final ExecutionContext context) throws ActionException {
		final String name = action.getName();
		try {
			return new SendGreetingResult(name, "Hello from the server");
		} catch (Exception cause) {
			throw new ActionException(cause);
		}
	}
 
	public void rollback(final SendGreeting action,
			final SendGreetingResult result, final ExecutionContext context)
			throws ActionException {
		// Nothing to do here
	}
 
	public Class<SendGreeting> getActionType() {
		return SendGreeting.class;
	}
 
//	
//	@Autowired
//	@Required
//	public void setCustomerService(CustomerService gibher) {
//
//	}
//
 
}

I am very happy with this approach as it allows me to add new action (command) handlers without any configuration overhead. Simply dropping the implementation somewhere in the server package will do all the magic.

8 comments to Use Spring with GWT dispatch

Leave a Reply