The Singhology's Blog

GWT 2.2.0 RequestFactory + Spring 3.0.x Integration

Posted in Spring Framework, GWT by jsinghfoss on August 10, 2011

MOVED from old blog

I must say it’s been a while since my last post. Lately, I have been really busy with work and part-time projects that I could not even reserve a minute to come here. But anyways today I’m here to reveal some really good work that I was stuck on for almost a week now. In my project, I had decided to implement GWT 2.2.0 along with Spring 3.0.x. Talking about communication: one way to enable communication between client and server is to use GWT RPC mechanism. But RPC from what I have learned is more of a service oriented approach. My first need was to have something in place for CRUD functionality. For data oriented tasks such as CRUD, Google has invented something called RequestFactory (RF). After skimming the Google tutorial, RF sounded like the right approach for CRUDing data. With RF we can also control the amount of data client will present on the UI. Now since I’m using Spring, the challenge was to discover a way to integrate RF with Spring. I spent hours researching on how to do this but couldn’t find a way. That being said there’s enough material available on Google in regards to RPC + Spring integration. After few days of hard luck, I finally invented a solution on how to do this. I would also like to give credit to “niloc132” who’s usually on #gwt and helped with GWT.

TWITTER: Please give credits where due and you can follow me on twitter at @jsinghfoss.

LICENSE: You are free to use this solution for personal or commercial use. You can also modify the solution in any way. In case if you have enhanced the solution, please share with everyone. Provided as-is without any guaranty.

EDIT 4/27/2011 I have noticed few bugs in the below source-code. Once all the bugs are fixed, I will edit this post to include changes. So, stay tuned!

EDIT 5/5/2011 Modified GWTSpringEntityLocator class. Changes include: class does not implement ApplicationContextAware interface anymore, Create method creates an instance of the entity, Removed unused context (as well as set method).

RequestFactory + Spring INTEGRATION SOLUTION

STEP 1 - web.xml

Declare DispatcherServlet and map it to “/gwtRequest

<servlet>
  <servlet-name>singhsolution</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>singhsolution</servlet-name>
  <url-pattern>/gwtRequest</url-pattern>
</servlet-mapping>

The above code will intercept all incoming requests whose url pattern contains “/gwtRequest“.

STEP 2 - singhsolution-servlet.xml

Create a servlet mapping file by the name “singhsolution-servlet.xml“. It will contain information related to the servlets managed by Spring.

Inside this file we need to declare a SimplUrlHandlerMapping bean which will map a value to its appropriate controller. In our case, the controller is called “gwtRequestFactoryController“.

<bean>
		<property name="mappings">
			<value>
				/gwtRequest=gwtRequestFactoryController
 			</value>
		</property>
	</bean>

Before we declare our “gwtRequestController” we need to create few classes that will be used by the client to find appropriate entities and services managed by Spring.

STEP 3 - Create the following classes

GWTSpringServiceLayerDecorator
GWTSpringEntityLocator
GWTSpringServiceLocator

public class GWTSpringServiceLayerDecorator extends ServiceLayerDecorator
		implements ApplicationContextAware {
	private ApplicationContext context;
	@Override
	public <T extends Locator<?, ?>> T createLocator(Class<T> clazz) {
		return context.getBean(clazz);
	}
	@Override
	public Object createServiceInstance(Method contextMethod
			Method domainMethod) {
		// Check if the request needs a service locator
		Class<? extends ServiceLocator> locatorType = getTop()
				.resolveServiceLocator(contextMethod, domainMethod);
		assert locatorType != null;
		// Inject an instance of the locator itself,
		// and then get an instance from it
		ServiceLocator locator = context.getBean(locatorType);
		return locator.getInstance(domainMethod.getDeclaringClass());
	}
	@Override
	public Object invoke(Method domainMethod, Object... args) {
		Object response = super.invoke(domainMethod, args);
		try {
			if (response instanceof Callable) {
				return ((Callable<?>) response).call();
			} else {
				return response;
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	public void setApplicationContext(ApplicationContext context)
			throws BeansException {
		this.context = context;
	}
}

GWTSpringEntityLocator will be used to locate JPA entities managed by Spring configuration files.

public class GWTSpringEntityLocator<T extends HasVersionAndId> extends
		Locator<T, Long> {
	@Autowired
	EntityManager entityManager;

	@Override
	public T create(Class<? extends T> clazz) {
		try
		{
			return clazz.newInstance();
		} catch (InstantiationException e)
		{
		       throw new RuntimeException(e);
		} catch (IllegalAccessException e)
		{
			throw new RuntimeException(e);
		}
	}

	@Override
	public T find(Class<? extends T> clazz, Long id) {
		return entityManager.find(clazz, id);
	}
	@Override
	public Class<T> getDomainType() {
		// Unused, and if it becomes used, we're in trouble
		throw new UnsupportedOperationException();
	}
	@Override
	public Long getId(T domainObject) {
		return domainObject.getId();
	}
	@Override
	public Class<Long> getIdType() {
		return Long.class;
	}
	@Override
	public Object getVersion(T domainObject) {
		return domainObject.getVersion();
	}
}

GWTSpringServiceLocator will be used to locate services managed by Spring configuration files.

public class GWTSpringServiceLocator implements ServiceLocator,
		ApplicationContextAware {
	private ApplicationContext context;
	public Object getInstance(Class<?> clazz) {
		return context.getBean(clazz);
	}
	public void setApplicationContext(ApplicationContext context)
			throws BeansException {
		this.context = context;
	}
}

STEP 4

In this step, we’ll implement a hack that I have created which will manage our RequestFactoryServlet (i.e. Spring will manage our RequestFactoryServlet) containing our customServiceLayerDecorator implementation.

CustomServletWrappingController

Please note that this is an initial hack and I am sure there is room for improvement. The negative side of this hack is we will have to pretty much rewrite the entire ServletWrappingController class as it’s animmutable class. The “afterPropertiesSet” method is where my hack is implemented. The stock implementation of this method creates an instance of a servlet defined in the Spring configuration file. But in my hack I’ve replaced the following line:

this .servletInstance = (Servlet) this .servletClass.newInstance();

to

this.servletInstance = (Servlet) this.getApplicationContext().getBean("requestFactoryServlet");

IMPORTANT NOTE: So as you can see, we are not using the servlet class defined in our Spring configuration file and instead hard coding the Servlet that we intend to use. Clearly, the downside of this approach is your CustomServletWrappingController can only manage one servlet i.e. your RequestFactoryServlet.

Hey but this works!

public class CustomServletWrappingController extends ServletWrappingController {
	private Class servletClass;
	private String servletName;
	private Properties initParameters = new Properties();
	private String beanName;
	private Servlet servletInstance;
	/**
	 * Set the class of the servlet to wrap. Needs to implement
	 * <code>javax.servlet.Servlet</code>.
	 *
	 * @see javax.servlet.Servlet
	 */
	public void setServletClass(Class servletClass) {
		this.servletClass = servletClass;
	}
	/**
	 * Set the name of the servlet to wrap. Default is the bean name of this
	 * controller.
	 */
	public void setServletName(String servletName) {
		this.servletName = servletName;
	}
	/**
	 * Specify init parameters for the servlet to wrap, as name-value pairs.
	 */
	public void setInitParameters(Properties initParameters) {
		this.initParameters = initParameters;
	}
	public void setBeanName(String name) {
		this.beanName = name;
	}
	/**
	 * Overriden to implement our hack. It initializes the wrapped Servlet
	 * instance.
	 *
	 * @author jatinder-singh
	 * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
	 */
	@Override
	public void afterPropertiesSet() throws Exception {
		if (this.servletClass == null) {
			throw new IllegalArgumentException("servletClass is required");
		}
		if (!Servlet.class.isAssignableFrom(this.servletClass)) {
			throw new IllegalArgumentException("servletClass ["
					+ this.servletClass.getName()
					+ "] needs to implement interface [javax.servlet.Servlet]");
		}
		if (this.servletName == null) {
			this.servletName = this.beanName;
		}
		// We retrieve RF instance instead of using the one defined in
		// singhsolution-servlet.xml
		// Author: Jatinder Singh
		this.servletInstance = (Servlet) this.getApplicationContext().getBean(
				"requestFactoryServlet");
		this.servletInstance.init(new DelegatingServletConfig());
	}
	/**
	 * Invoke the the wrapped Servlet instance.
	 *
	 * @see javax.servlet.Servlet#service(javax.servlet.ServletRequest,
	 *      javax.servlet.ServletResponse)
	 */
	@Override
	protected ModelAndView handleRequestInternal(HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		this.servletInstance.service(request, response);
		return null;
	}
	/**
	 * Destroy the wrapped Servlet instance.
	 *
	 * @see javax.servlet.Servlet#destroy()
	 */
	public void destroy() {
		this.servletInstance.destroy();
	}
	/**
	 * Internal implementation of the ServletConfig interface, to be passed to
	 * the wrapped servlet. Delegates to ServletWrappingController fields and
	 * methods to provide init parameters and other environment info.
	 */
	private class DelegatingServletConfig implements ServletConfig {
		public String getServletName() {
			return servletName;
		}
		public ServletContext getServletContext() {
			return CustomServletWrappingController.this.getServletContext();
		}
		public String getInitParameter(String paramName) {
			return initParameters.getProperty(paramName);
		}
		public Enumeration getInitParameterNames() {
			return initParameters.keys();
		}
	}
}

If you are here you are pretty much there :) . Now edit your singhsolution-servlet.xml file to include all the beans you have created above.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
	<bean>
		<property name="mappings">
			<value>
				/gwtRequest=gwtRequestFactoryController
 			</value>
		</property>
	</bean>
	<bean id="gwtSpringServiceLayerDecorator" 		class="com.jsi.singhsolution.server.servlet.GWTSpringServiceLayerDecorator" />
	<bean id="defaultExceptionHandler" 		class="com.google.gwt.requestfactory.server.DefaultExceptionHandler" />
	<!-- RequestFactoryServlet bean is scoped as singleton. Injecting custom  		SpringServiceLayerDecorator. -->
	<bean id="requestFactoryServlet" 		class="com.google.gwt.requestfactory.server.RequestFactoryServlet" 		scope="singleton">
		<constructor-arg ref="defaultExceptionHandler" />
		<constructor-arg ref="gwtSpringServiceLayerDecorator" />
	</bean>
	<bean id="gwtSpringEntityLocator" 		class="com.jsi.singhsolution.server.servlet.GWTSpringEntityLocator" />
	<bean id="gwtSpringServiceLocator" 		class="com.jsi.singhsolution.server.servlet.GWTSpringServiceLocator" />
	<!-- IMPORTANT NOTE: CustomServletWrappingController is a hacked version  		of ServletWrappingController. The servlet class defined here will not be  		used. Please see CustomServletWrappingController for more information. -->
	<bean id="gwtRequestFactoryController" 		class="com.jsi.singhsolution.server.servlet.CustomServletWrappingController">
		<property name="servletClass">
			<value>com.google.gwt.requestfactory.server.RequestFactoryServlet
			</value>
		</property>
		<property name="servletName">
			<value>gwtRequest</value>
		</property>
	</bean>
</beans>

If you have followed my instructions carefully, you have created all the required classes including the hack that is needed for Spring to manage RequestFactoryServlet in its machinery.

RequestFactory in Action

I am going to assume you know a little bit of GWT. The below code will end up calling a Spring Service called UserDetailsService. Once the user is returned successfully we display its first name.

requestFactory.userDetailsRequest().getUserByUsername("singh").fire( new Receiver<UserEntityProxy>() {
					@Override
					public void onSuccess(UserEntityProxy user) {
						RootPanel.get("result").add(new HTML("Username was retrieved: " + user.getFirstName()));
					}
				});

How this hack can be improved?

If I could inject my custom ServiceLayerDecorator class in the RequestFactoryServlet class defined in the actual ServletWrappingController bean, will solve our issue of hardcoding the Servlet; and we won’t need a CustomServletWrappingController that we have currently in place.

I don’t really have a code sample for this yet but I may create one in future. If you think the above code can be improved, please drop a comment or email me at j.singh.developer@gmail.com.

 

Advertisement

5 Responses

Subscribe to comments with RSS.

  1. Jeff Larsen (@larsenje) said, on August 10, 2011 at 7:54 pm

    A somewhat simpler solution that I came across (and this is pretty much out of the box in 2.4).

    Extend RequestFactoryServlet and add a ThreadLocal to the class, set it in the onPost method.

    For an example, here is the trunk version of RequestFactoryServlet.

    http://code.google.com/p/google-web-toolkit/source/browse/trunk/user/src/com/google/web/bindery/requestfactory/server/RequestFactoryServlet.java

    Then your SpringServiceLocator is relatively simple.

    Then you just need to

    public class SpringServiceLocator implements ServiceLocator {

    public Object getInstance(Class arg0) {
    ApplicationContext ctx = WebApplicationContextUtils
    .getWebApplicationContext(RequestFactoryServlet.getThreadLocalServletContext());
    return ctx.getBean(arg0);
    }
    }

    Then in every RequestContext you just have to specify locator=SpringServiceLocator.class and you’re set.

    There might be a defiency here in that RequestFactory doesn’t actually use Spring, There might be some deficiencies here and if so I would love to hear them, but so far this has been working fine in our implementation.

    • -sowdri- said, on August 16, 2011 at 6:44 am

      @Jeff Larsen

      Even i ‘m using the same solution. And it works like a charm,, so far i’ve not encountered any deficiencies.. and the best part is I don’t have to write a dispatcher servlet.

      Thanks,

    • jsinghfoss said, on August 17, 2011 at 5:15 am

      Good stuff guys. Will try and edit this post for new info. Thanks

  2. iddo said, on August 30, 2011 at 11:55 am

    Great post! took me 30 minutes to set is up.

    I’ve got 2 questions tho:
    1. Using Jeff Larsen’s tip, is there any other need in CustomServletWrappingController?
    2. What are the best practices of unit-testing RF services calling to Spring?

  3. jim said, on January 2, 2012 at 6:39 pm

    I tried to integrate this sample by using GWT2.4. I got some issues regarding GWTSpringServiceLayerDecorator. In 2.4, the api changed to public Object createServiceInstance(Class requestContext) {..}

    Could you provide a impl for this ?


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.