Wednesday, September 30, 2009

Configuring cometd with Spring

I've been having fun hacking on cometd related things.

Just thought I'd post the details on how I got it work. I wanted to try and do the spring configuration with as little custom code as possible so I could keep up to date with the latest ContinuationCometdServlet.

Any suggestions for improvements on this process are appreciated. The timing of things initializing is important.

1: Configure the ContinuationCometdServlet (cometd) servlet as usual in web.xml just make sure it has a load-on-startup property set.

2: An important thing we need to put into our spring applicationContext.xml is:

<bean id="bayeux" class="org.cometd.server.continuation.ContinuationBayeux" />

<bean class="org.springframework.web.context.support.ServletContextAttributeExporter">
<property name="attributes">
<map>
<entry key="org.cometd.bayeux"><ref bean="bayeux"/></entry>
</map>
</property>
</bean>

This creates the Bayeux object and puts it into the ServletContext before the cometd servlet starts up.

3: Configure your Bayeux Services

<bean id="testService" class="com.testpackage.TestService" lazy-init="true" >
<constructor-arg><ref bean="bayeux"/></constructor-arg>
</bean>

<bean id="testService2" class="com.testpackage.TestService2" lazy-init="true" >
<constructor-arg><ref bean="bayeux"/></constructor-arg>
</bean>

<bean id="bayeuxServiceList" class="org.springframework.beans.factory.config.ListFactoryBean" lazy-init="true">
<property name="sourceList">
<list>
<ref bean="testService" />
<ref bean="testService2" />
</list>
</property>
</bean>

The important part here is that the services and the List holding them are set to lazy-init. The Bayeux object for the service constructors wont be ready until after the cometd servlet initializes.

4: The one custom piece of code I'm not sure how to do without yet is a servlet that simply initializes the spring configured Bayeux services after the Bayeux object has been properly initialized by the cometd servlet.

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
public class SpringBayeuxInitServlet extends GenericServlet
{

@Override
public void service (ServletRequest req, ServletResponse res) throws ServletException, IOException
{
((HttpServletResponse)res).sendError(503);
}

@Override
public void init () throws ServletException
{
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
wac.getBean ("bayeuxServiceList");
}
}

This servlet must have a load-on-startup property set to initialize after the cometd just as the chat demo servlet does.