Archive for March, 2010

Communication between C# .NET and Java via ActiveMQ messaging – Part 3

The Java server side – ‘same same … but different!’

For C# client example see Part 2.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:amq="http://activemq.apache.org/schema/core"
	xmlns:jms="http://www.springframework.org/schema/jms"
	xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core-5.3.0.xsd
http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-2.5.xsd">
 
   ...
 
<!--  define broker & connectors -->
  <amq:broker persistent="false" useJmx="false">
    <amq:transportConnectors>
      <amq:transportConnector uri="tcp://localhost:61616" />
    </amq:transportConnectors>
  </amq:broker>
 
<!--  define connection factory -->
  <amq:connectionFactory id="jmsConnectionFactory" brokerURL="vm://localhost" />
  <amq:queue id="test.queue" physicalName="test.queue" />
 
     <!-- A cached connection factory to wrap the default factory -->
  <bean id="cachedConnectionFactory"
      class="org.springframework.jms.connection.CachingConnectionFactory"
      p:targetConnectionFactory-ref="jmsConnectionFactory"
      p:sessionCacheSize="10" />
 
 
  <jms:listener-container connection-factory="cachedConnectionFactory" destination-type="queue">
    <jms:listener destination="test.queue" ref="MessageReceiverListener" />
  </jms:listener-container>
 
  <bean id="ExampleMessageHandler" class="it.koller.interopexample.handlers.ExampleMessageHandler" />
  <bean id="ExampleMessageConverter" class="it.koller.interopexample.converter.ExampleMessageConverter"/>
 
<bean id="MessageReceiverListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
 <property name="defaultListenerMethod" value="handleObject"/>
   <property name="delegate" ref="ExampleMessageHandler" />
   <property name="messageConverter" ref="ExampleMessageConverter" />
</bean>
 
   ...
 
</beans>

We also need an ExampleMessageConverter as we have it at the C# client side.

package it.koller.interopexample.converter;
 
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.Session;
 
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jms.support.converter.MessageConversionException;
import org.springframework.jms.support.converter.MessageConverter;
 
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import it.koller.interopexample.ExampleMessage;
 
public class ExampleMessageConverter implements MessageConverter {
 
	protected Log logger = LogFactory.getLog(SoCamMessageConverter.class);
 
	@Override
	public Object fromMessage(Message mes) throws JMSException,
			MessageConversionException {
		if(logger.isDebugEnabled())
			logger.debug("fromMessage()");
        ExampleMessage examplemsg = new ExampleMessage();
        BASE64Decoder decoder = new BASE64Decoder();
        MapMessage mm = (MapMessage)mes;
        if examplemsg == null)
        {
            throw new MessageConversionException("ExampleMessageConverter can not convert object of type " +
            		mes.getJMSType());
        }
        try
        {
            examplemsg.setMessagetext(mm.getString("messageText"));
 
            if(mm.getString("pictureBytes")!=null)
            	examplemsg.setPictureBytes(decoder.decodeBuffer(mm.getString("pictureBytes")));
 
    		if(logger.isDebugEnabled())
    			logger.debug("got Examplemessage: "+examplemsg);
            return tradeRequest;
 
         } catch (Exception e)
         {
            throw new MessageConversionException("Could not convert ExampleMessage", e);
         }
	}
 
	@Override
	public Message toMessage(Object mesobj, Session session) throws JMSException,
			MessageConversionException {
		if(logger.isDebugEnabled())
			logger.debug("toMessage()");
        ExampleMessage examplemsg = (ExampleMessage) mesobj;
        BASE64Encoder encoder = new sun.misc.BASE64Encoder();
        if examplemsg == null)
        {
            throw new MessageConversionException("ExampleMessageConverter can not convert object of type " +
            		mesobj.getClass());
        }
        try
        {
            MapMessage mm = session.createMapMessage();
            mm.setString("messageText", examplemsg.getMessagetext());
            if(examplemsg.getPictureBytes()!=null)
            	mm.setString("pictureBytes", encoder.encode(examplemsg.getPictureBytes()));
            return mm;
 
         }
        catch (Exception e)
         {
            throw new MessageConversionException("Could not convert ExampleMessage to message", e);
         }
	}
 
}

To receive messages from the test.queue a listener and listener-container have to be configured.
The listener-container needs a connection factory and the required destination-type (topic or queue) set.

 
   ...
 
  <jms:listener-container connection-factory="cachedConnectionFactory" destination-type="queue">
    <jms:listener destination="test.queue" ref="MessageReceiverListener" />
  </jms:listener-container>
 
   ...

The listener class has to be a MessageListener, which means it has to implement the according interface.

public interface MessageListener {
 
        public void onMessage(Message message);
}

I use a default MessageListenerAdapter provided by the Spring framework and inject my custom MessageConverter as well as a MessageHandler.

 
   ...
 
<bean id="MessageReceiverListener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
 <property name="defaultListenerMethod" value="handleObject"/>
   <property name="delegate" ref="ExampleMessageHandler" />
   <property name="messageConverter" ref="ExampleMessageConverter" />
</bean>
 
   ...

The received Message is passed on to the toObject method of the ExampleMessageConverter, which extracts the data from the MapMessage and returns a ExampleMessage object.
The ExampleMessage is then handed over to the ExampleMessageHandler and can be processed by the server application.

package it.koller.interopexample.receiver;
import org.apache.log4j.Logger;
 
public class ExampleMessageHandler {
 
	private Logger logger = Logger.getLogger(ExampleMessageHandler.class);
 
	public void handleObject(Object mesobj)
 
		if(logger.isDebugEnabled())
			logger.debug("ExampleMessageHandler handleObject "+mesobj);
 
		...
                // do something ;)
	}
 
}

The weak point in this (very) simple approach is the MessageConverter. Both are coded manually and therefore a possible source of error.
To generate the POJO/PONO and the converter from the examplemessage.xsd would provide much more safety and convenience.
Commercial message provider support this feature, i guess.

Please feel free to leave any comments!

, , , , ,

1 Comment