1 import org.springframework.context.ApplicationEvent; 2 import org.apache.commons.lang.builder.ToStringBuilder; 3 import org.apache.commons.lang.builder.ToStringStyle; 4 import java.util.Map; 5 import java.util.HashMap; 6 import java.util.Collection; 7 8 public class MessageEvent extends ApplicationEvent { 9 private String msg; 10 private Map<String, String> map; 11 public MessageEvent(final Object source, final String ... properties) { 12 super(source); 13 map = new HashMap<String, String>(); 14 for (int i=0; i<properties.length-1; i+=2) { 15 String name = properties[i]; 16 String value = properties[i+1]; 17 map.put(name, value); 18 } 19 } 20 public MessageEvent(final Object source, final Map<String, String> map) { 21 super(source); 22 this.map = map; 23 } 24 public String getProperty(final String key) { 25 return map.get(key); 26 } 27 public boolean isProperty(final String key) { 28 String value = map.get(key); 29 if (value == null) { 30 return false; 31 } 32 return new Boolean(value).booleanValue(); 33 } 34 public Map<String, String> getProperties() { 35 return map; 36 } 37 @Override 38 public String toString() { 39 return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) 40 .append("source", getSource()) 41 .append("map", this.map) 42 .toString(); 43 } 44 } 45 46
I then created a class to convert above message into JMS message, e.g.
1 import javax.jms.Session; 2 import javax.jms.JMSException; 3 import javax.jms.Message; 4 import javax.jms.MapMessage; 5 6 import org.springframework.jms.support.converter.MessageConversionException; 7 import org.springframework.jms.support.converter.MessageConverter; 8 9 import java.util.Map; 10 import java.util.HashMap; 11 import java.util.Enumeration; 12 13 public class MapMessageConverter implements MessageConverter { 14 public Object fromMessage(final Message message) throws JMSException, MessageConversionException { 15 if (!(message instanceof MapMessage)) { 16 throw new MessageConversionException("Message isn't a MapMessage"); 17 } 18 MapMessage mapMessage = (MapMessage) message; 19 Map<String, String> map = new HashMap<String, String>(); 20 Enumeration<String> en = mapMessage.getMapNames(); 21 while (en.hasMoreElements()) { 22 String name = en.nextElement(); 23 String value = mapMessage.getString(name); 24 map.put(name, value); 25 } 26 return map; 27 } 28 public Message toMessage(final Object object, final Session session) throws JMSException, MessageConversionException { 29 if (!(object instanceof Map)) { 30 throw new MessageConversionException("Object isn't a Map"); 31 } 32 Map<String, String> map = (Map<String, String>) object; 33 MapMessage message = session.createMapMessage(); 34 for (Map.Entry<String, String> e : map.entrySet()) { 35 message.setString(e.getKey(), e.getValue()); 36 } 37 return message; 38 } 39 } 40
Next I created a class that listened for Spring application event and converted into JMS message and published it:
1 import javax.jms.JMSException; 2 import javax.jms.Session; 3 import javax.jms.Message; 4 import java.util.Map; 5 import java.util.UUID; 6 import org.apache.log4j.Logger; 7 import org.springframework.jms.core.JmsTemplate; 8 import org.springframework.jms.core.MessageCreator; 9 import org.springframework.jms.support.converter.MessageConverter; 10 import org.springframework.context.ApplicationEvent; 11 import org.springframework.context.ApplicationListener; 12 13 public class PublisherAdapter implements ApplicationListener { 14 private static final Logger logger = Logger.getLogger(PublisherAdapter.class); 15 private JmsTemplate jmsTemplate; 16 private MessageConverter converter; 17 18 // 19 public void setMessageConverter(final MessageConverter converter) { 20 this.converter = converter; 21 } 22 23 public void setJmsTemplate(final JmsTemplate jmsTemplate) { 24 this.jmsTemplate = jmsTemplate; 25 } 26 27 public void publish(final Map<String, String> map) { 28 jmsTemplate.send(new MessageCreator() { 29 public Message createMessage(final Session session) throws JMSException { 30 return converter.toMessage(map, session); 31 } 32 }); 33 } 34 35 public void onApplicationEvent(final ApplicationEvent event) { 36 if (event.getSource() != converter && event instanceof MessageEvent) { 37 publish(((MessageEvent)event).getProperties()); 38 } 39 } 40 } 41 42
Then, I created a JMS listener that listened for messages on Topic and converted those into Spring application event:
1 import org.apache.log4j.Logger; 2 import java.util.Map; 3 import javax.jms.MessageListener; 4 import javax.jms.Message; 5 import javax.jms.MapMessage; 6 import javax.jms.JMSException; 7 import org.springframework.jms.support.converter.MessageConverter; 8 import org.springframework.context.ApplicationContext; 9 import org.springframework.context.ApplicationContextAware; 10 import org.springframework.beans.BeansException; 11 12 13 public class ListenerAdapter implements MessageListener, ApplicationContextAware { 14 private static final Logger logger = Logger.getLogger(ListenerAdapter.class); 15 private MessageConverter converter; 16 private ApplicationContext applicationContext; 17 18 public void setMessageConverter(final MessageConverter converter) { 19 this.converter = converter; 20 } 21 public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException { 22 this.applicationContext = applicationContext; 23 24 } 25 26 public void onMessage(final Message message) { 27 Map<String, String> map = (Map<String, String>) converter.fromMessage(message); 28 applicationContext.publishEvent(new MessageEvent(this, map)); 29 } 30 } 31 32
Next, here is Spring configuration to bootstrap these listeners:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans> 3 <bean id="mapMessageConverter" class="com.amazon.jasper.messaging.spring.MapMessageConverter"/> 4 <bean id="springTopic" class="org.apache.activemq.command.ActiveMQTopic"> 5 <constructor-arg index="0" value="springTopic"/> 6 </bean> 7 <bean id="springJmsTemplate" class="org.springframework.jms.core.JmsTemplate" scope="prototype"> 8 <property name="connectionFactory" ref="jmsConnectionFactory"/> 9 <property name="deliveryPersistent" value="true"/> 10 <property name="messageConverter" ref="mapMessageConverter"/> 11 <property name="defaultDestination" ref="springTopic"/> 12 </bean> 13 <bean id="publisherAdapter" class="com.amazon.jasper.messaging.spring.PublisherAdapter" scope="prototype"> 14 <property name="jmsTemplate" ref="springJmsTemplate"/> 15 <property name="messageConverter" ref="mapMessageConverter"/> 16 </bean> 17 <bean id="springTopicListener" class="com.amazon.jasper.messaging.spring.ListenerAdapter" scope="prototype"> 18 <property name="messageConverter" ref="mapMessageConverter"/> 19 </bean> 20 <bean class="org.springframework.jms.listener.DefaultMessageListenerContainer" init-method="start" destroy-method="stop" scope="prototype"> 21 <property name="connectionFactory" ref="jmsConnectionFactory"/> 22 <property name="destination" ref="springTopic"/> 23 <property name="messageListener" ref="springTopicListener"/> 24 <property name="transactionManager" ref="jmsTransactionManager"/> 25 <property name="concurrentConsumers" value="10"/> 26 </bean> 27 </beans> 28 29
Finally, here is how you will actually use this plumbing in your code:
1 import org.springframework.context.ApplicationContext; 2 import org.springframework.context.ApplicationContextAware; 3 import org.springframework.context.ApplicationEvent; 4 import org.springframework.context.ApplicationListener; 5 import com.amazon.jasper.workflow.WorkflowContext; 6 public class Myclass implements ApplicationListener, ApplicationContextAware { 7 private ApplicationContext ctx; 8 9 // ... 10 ctx.publishEvent(new MessageEvent(this, SYNC_ID, syncId, SYNC_XPDL, "true")); 11 12 public void setApplicationContext(ApplicationContext applicationContext) { 13 this.ctx = applicationContext; 14 } 15 16 public void onApplicationEvent(ApplicationEvent event) { 17 if (event instanceof MessageEvent) { 18 MessageEvent msgEvt = (MessageEvent)event; 19 // do cache coherence 20 } 21 } 22 23
All you need is to add your class to your Spring configuration file, it will automatically be registered as listener for spring events. All this is fairly simple, but I hope it helps you for similar uses in your code.