Friday, October 23, 2009

JMS and Spring 3.0 rc1 and some recommendations

So, I got to play with Spring and ActiveMQ this sprint for a little application we are writing at work. I've been looking for a project which could use JMS for a while, and this one seemed to fit the bill perfectly. Like I said in my last post, I found the experience doing the configuration to be very straight forward and there were lots of information out there to set this up (especially Spring in Action).

The few pieces that I found scattered about in many places which I would recommend are:
  1. Use a pooled connection factory with Spring. The JMS Template is rather heavy as it opens and closes a connection every message as well as other over head. A serious waste and ActiveMQ recommends using theirs.
  2. With the message listener, be sure to include the destroy-method="shutdown" attribute as without it, there is no guarantee that your consumers will be terminated correctly (a problem we ran into without realizing it for a few days)
  3. Add maxConcurrentConsumers to increase the number of consumers to help process the messages. This will dynamically change the number of consumers based on need and has tested to be a big win for us.
Otherwise, this is really simple to get up and running. It has been a HUGE help in terms of performance and seems to be very stable under our load tests thus far. I included my configuration below in case you would like to use it as a template.

Some resources:
http://activemq.apache.org/
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/ch21.html

<bean id="destination" class="org.apache.activemq.command.ActiveMQQueue">
   <constructor-arg index="0" value="${activemq.destination.name}">
</bean>

<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
   <property name="connectionFactory" ref="jmsFactory">
   <property name="defaultDestination" ref="pruneDestination">
</bean>

<bean id="jmsFactory" class="org.apache.activemq.pool.PooledConnectionFactory" method="stop">
   <property name="connectionFactory">
   <bean class="org.apache.activemq.ActiveMQConnectionFactory">
      <property name="brokerURL" value="${activemq.broker.url}">
   </bean>
   </property>
</bean>

<bean id="messageListener" class="some.path.to.a.MessageListener">
   <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer" method="shutdown">
   <property name="connectionFactory" ref="jmsFactory">
   <property name="destination" ref="destination">
   <property name="messageListener" ref="messageListener">
   <property name="maxConcurrentConsumers" value="5">
</bean>

XStream and Spring 3.0

I have spent my last three weeks happily using Spring 3 rc1 getting to know some new features and using some old features that I have not had the opportunity to use before. Specifically I enjoyed the greatly reduced xml used when using annotations. Also, the REST handling in the controllers is worth the move.

However, I ran into a bit of problem when trying to use Spring's ContentNegotiatingViewResolver to return XML. I quickly got Spring wired to use a Marshalling view and I chose the XStreamMarshaller. Everything was going great. Development was quick and easy. I had JSON and XML responses going quickly (I did find a bug with declaring the defaultContentType, but they say they will fix this asap). I was able to focus on the business problem I was trying to solve instead of getting bogged down in framework muck.

Then I noticed a slight problem with my results in XML, there was no XML declaration in the response ( was missing). Going over the documentation lead me to the fact that the MarshallingView assumes that the Marshaller knows how to write the response document, but in this case, the Marshaller is an XStream implementation, and the XStream guys explicitly say "this is not my problem".

Seems like a case of the hot potato being passed back and forth. I didn't find anything in the documentation or forums on how to fix this. It feels like this should just be a boolean or String property to be set on the XStreamMarshaller which Spring provides, but it is not (maybe in 3.1?).

So I created a hack to fix this, I extended XStreamMarshaller and over wrote the following method:

/**
* {@inheritDoc}
*/
@Override
protected void marshalWriter(Object graph, Writer writer) throws XmlMappingException, IOException {
writer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
super.marshalWriter(graph, writer);
}

My config file looks like this:
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
</map>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
<bean class="org.springframework.web.servlet.view.xml.MarshallingView">
<constructor-arg ref="xstream" />
</bean>
</list>
</property>
<property name="defaultContentType" ref="jsonMediaType" />
<property name="ignoreAcceptHeader" value="true" />
</bean>

<bean id="xstream" class="some.path.to.your.class.which.extends.XStreamMarshaller" >
<property name="autodetectAnnotations" value="true" />
<property name="converters">
<array>
<bean id="someConverter" class="some.path.to.your.class.which.implementds.Converter" />
</array>
</property>
</bean>

It feels hacky, and I hate the solution, but I couldn't find another way to accomplish this task. Am I totally missing something here? Is there a simpler solution? Anyway, I hope this helps someone out down the line.