GWT Spring Integration - using ContextLoaderListener

Update: I have put together a sample app to demonstrate the integration technique: http://seewah.blogspot.com/2009/06/gwt-and-spring-security-sample-demo.html Just take out applicationContext-security.xml if you are not interested in the Spring Security aspect!

Just a quick followup post to my last post http://seewah.blogspot.com/2008/07/gwt-and-spring.html.

In the last post I demostrated how to use the SpringApplicationContextLoader to manually load an application context from an xml file and subsequently to access Spring-managed beans via this class inside DependencyInjectionRemoteServiceServlet.

For one reason or another, I have moved away from this approach and gone back to the traditional means of using the org.springframework.web.context.ContextLoaderListener in web.xml to load application contexts. So this is what to do if you prefer this approach.

First of all, create the listener as you would with a normal Spring web application


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


in web.xml. If you are running in host mode, this is found under the tomcat/webapps/ROOT/WEB-INF folder inside your project. Otherwise, just edit the usual web.xml inside your webapps folder in your tomcat installation.

Now, inside DependencyInjectionRemoteServiceServlet, simply use the static org.springframework.web.context.support.WebApplicationContextUtils.getWebApplicationContext(ServletContext sc) method to access the loaded application context. This following is the revised class:


import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
/**
* {@link RemoteServiceServlet} that automatically injects IoC dependency.
* "org.springframework.beans.factory.annotation.Autowired" annotation is used
* for marking which fields to inject into. Uses
* {@link SpringApplicationContextLoader} to retrieve beans by name.
* <p>
* Note that the current implementation will only inject "declared" fields, and
* not inherited fields. Fields can be private, protected, package or public.
*
* @author See Wah Cheng
* @created 27 Jun 2008
*/
@SuppressWarnings("serial")
public class DependencyInjectionRemoteServiceServlet extends RemoteServiceServlet {

protected static Logger logger = Logger.getLogger(DependencyInjectionRemoteServiceServlet.class);

@Override
public void init() throws ServletException {
super.init();
doDependencyInjection();
}

/**
* Carries out dependency injection. This implementation uses Spring IoC
* container.
*
* @exception NoSuchBeanDefinitionException
* if a suitable bean cannot be found in the Spring application
* context. The current implementation looks up beans by name
*/
protected void doDependencyInjection() {

for (Field field : getFieldsToDependencyInject()) {
try {
boolean isFieldAccessible = field.isAccessible();
if (!isFieldAccessible) {
field.setAccessible(true);
}
field.set(this, WebApplicationContextUtils.getWebApplicationContext(getServletContext()).getBean(field.getName()));
if (!isFieldAccessible) {
field.setAccessible(false);
}
logger.debug("Dependency injection successful: " + this.getClass().getName() + "." + field.getName());
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}

/**
* Find annotated fields to inject.
*
* @return a list of all the annotated fields
*/
private Set<Field> getFieldsToDependencyInject() {
Set<Field> fieldsToInject = new HashSet<Field>();
Field[] fields = this.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.getAnnotation(Autowired.class) != null) {
fieldsToInject.add(field);
}
}
return fieldsToInject;
}
}


Note that there are two changes to this class. First of all, doDependencyInjection() now accesses the application context using the org.springframework.web.context.support.WebApplicationContextUtils class as described above. Secondly, doDependencyInjection() is now invoked from inside the servlet's init() method and not its constructor. This is because, when org.springframework.web.context.support.WebApplicationContextUtils.getWebApplicationContext(ServletContext sc) is invoked inside doDependencyInjection(), a reference to the servlet's ServletContext must be obtained, and this is only available after the servlet object has been constructed.

Comments

Anonymous said…
when compiling with eclipse
on this line

for (Field field : getFieldsToDependencyInject())


i get error Type mismatch: cannot convert from element type Object to Field
See Wah Cheng said…
Hi sorry I forgot to escape the < and the > in the HTML so Set<Field> became Set. I have updated the post, please refer the class again.

I have also put the code into a scrollable pane, which hopefully should make it more readable.
Anonymous said…
so you suggested to extend the class you have written? why do we not use GWT-SL(http://gwt-widget.sourceforge.net/) instead, which is much simpler and clearer?
See Wah Cheng said…
I have not used GWT-SL personally, so I cannot really comment on it, but essentially the GWT-SL way and my way differs in terms of whether the integration logic happens on the Spring side or the GWT side.

At the time when I came up with this solution, exporting spring beans involved extending the GWTSpringController, which I did not really like. I wanted to keep my Spring layer as clean as possible and write my own GWT RPC servlets to access simple Spring service beans instead of using Spring MVC to "export" these services. It is simply a different way of doing things. Doing it my way means Spring developers do not have to care/know anything about GWT integration, and GWT developers have total control on how to use these service beans.

Since then they have introduced GWTRPCServiceExporter, which seems less invasive. Again it does not change the fundamental fact that the integration logic happen on the Spring side and not the GWT side.

As I said in my original article, there are so many different ways of tackling this problem. I just want to give people some ideas to create solutions that suit their particular needs.
Unknown said…
Hi, It's a term that makes some small business owners cringe. The mere mention of website marketing or SEO to Web Design Cochin business owners is most often met with fear and loathing.Thanks....

Popular Posts