Archive for February, 2010

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

The C# client side.

In Part 1 we defined and generated the ExampleMessage POJO/PONO.

We have to configure the logging part to enable the debug/info output getting Spring.NET to work.

   ...
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 
  <configSections>
    <sectionGroup name="common">
      <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
    </sectionGroup>
    <sectionGroup name="spring">
      <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
      <section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/>
      <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core"/>
    </sectionGroup>
  </configSections>
 
 
  <common>
    <logging>
      <factoryAdapter type="Common.Logging.Simple.ConsoleOutLoggerFactoryAdapter, Common.Logging">
        <arg key="level" value="DEBUG" />
        <arg key="showLogName" value="true" />
        <arg key="showDataTime" value="true" />
        <arg key="dateTimeFormat" value="yyyy/MM/dd HH:mm:ss:fff" />
      </factoryAdapter>
    </logging>
  </common>
 
  <spring>
 
   ...
 
    <objects>
      <import resource="objects.xml"/>
    </objects>
 
  </spring>
 
</configuration>

objects.xml looks like:

 
<objects xmlns="http://www.springframework.net"
  xmlns:nms="http://www.springframework.net/nms">
 
<object id="ActiveMqConnectionFactory" type="Apache.NMS.ActiveMQ.ConnectionFactory, Apache.NMS.ActiveMQ">
    <constructor-arg index="0" value="tcp://localhost:61616"/>
  </object>
 
  <!-- use caching connection factory  -->
  <object id="ConnectionFactory" type="Spring.Messaging.Nms.Connections.CachingConnectionFactory, Spring.Messaging.Nms">
    <constructor-arg index="0" ref="ActiveMqConnectionFactory" />
    <property name="SessionCacheSize" value="10"/>
  </object>
 
  <!-- message converter for hashmap-message <=> examplemessage -->
  <object id="ExampleMessageConverter" type="ClientApp.ExampleMessageConverter"/>
 
  <!-- template for automatic conversion -->
  <object id="NmsTemplate" type="Spring.Messaging.Nms.Core.NmsTemplate">
    <property name="ConnectionFactory" ref="ConnectionFactory" />
    <property name="DefaultDestinationName" value="test.queue" />
    <property name="SessionAcknowledgeMode" value="AutoAcknowledge" />
    <property name="MessageConverter" ref="ExampleMessageConverter" />
  </object>
 
  <!-- service ;) to send  -->
  <object name="ExempleService" type="ClientApp.ExampleService">
    <property name="NmsTemplate"  ref="NmsTemplate"/>
  </object>
 
   ...
 
</objects>

Lets go top down.

 
<objects xmlns="http://www.springframework.net"
  xmlns:nms="http://www.springframework.net/nms">
 
   ...
 
  <!-- service ;) to send  -->
  <object name="ExempleService" type="ClientApp.ExampleService">
    <property name="NmsTemplate"  ref="NmsTemplate"/>
  </object>
 
 
   ...
 
</objects>

To provide the message sending functionality to the client application lets create a simple “service”.

using System;
using System.Collections.Generic;
using System.Text;
using Spring.Messaging.Nms.Core;
using Common.Logging;
 
namespace ClientApp
{
    public class ExampleService : NmsGatewaySupport
    {
        private ILog logger = LogManager.GetLogger(typeof(ExampleService ));
 
        public ExampleService()
        {
            logger.Info("ExampleService created");
        }
 
        public void send(ExampleMessage mes)
        {
            if(logger.IsDebugEnabled)
                 logger.Debug("sending ExampleMessage "+mes);
            NmsTemplate.ConvertAndSend(mes);
        }
    }
}

The implementation of ExampleService inherits from Spring.Messaging.Nms.Core.NmsGatewaySupport which is a helper class providing easy access to a NmsTemplate.
The NmsTemplate is similar to JmsTemplate from the Java Spring world.
We have to set some properties such as a ConnectionFactory for NmsConnctions and a destination to specify which Topic/Queue the message should be sent to.

<objects xmlns="http://www.springframework.net"
  xmlns:nms="http://www.springframework.net/nms">
 
   ...
 
  <!-- template for message sending-->
  <object id="NmsTemplate" type="Spring.Messaging.Nms.Core.NmsTemplate">
    <property name="ConnectionFactory" ref="ConnectionFactory" />
    <property name="DefaultDestinationName" value="test.queue" />
    <property name="SessionAcknowledgeMode" value="AutoAcknowledge" />
   ...
  </object>
 
 
   ...
 
</objects>

JMS specifies TextMessages, MapMessages and ObjectMessages. I decided to use a MapMessage for exchanging data beween client and server.
NMS tries to follow the JMS standard, but there is no de facto standard messaging API in .NET
To enable convertion between ExampleMessage object and MapMessage object we make use of the MessageConverter interface.

namespace Spring.Messaging.Nms.Support.Converter
{
   public interface IMessageConverter
   {
      public Object FromMessage(IMessage mes);
      public IMessage ToMessage(Object mesobj, ISession session);
   }
}

The ExampleMessageConverter functionality is very simple. The ToMessage code has to take care of putting all the transmit data from the ExampleMessage to the MapMessage.
The FromMessage do it vice versa. I assumed there is a SetBytes method like in JMS MapMessage interface.
Thought wrong! Did i overlook something maybe? Nevermind!
A conversion from a byte array to a Base64 encoded string is also quite easy.

using System;
using System.Collections.Generic;
using System.Text;
using Spring.Messaging.Nms.Support.Converter;
using Apache.NMS;
using Apache.NMS.ActiveMQ;
using Common.Logging;
 
namespace ClientApp
{
 
    public class ExampleMessageConverter : IMessageConverter
    {
        ILog logger = LogManager.GetLogger(typeof(ExampleMessageConverter));
 
	// called to convert a JMS/NMS message to a object
        public Object FromMessage(IMessage mes)
        {
 
            logger.Info("fromMessage()");
            ExampleMessage examplemsg= new ExampleMessage();
            IMapMessage mm = (IMapMessage)mes;
            if (examplemsg == null)
            {
                throw new MessageConversionException("ExampleMessageConverter can not convert object of type " +
                        mes.GetType());
            }
            try
            {
                examplemsg.Messagetext = mm.Body.GetString("messageText");
                if(mm.Body.GetString("pictureBytes")!=null)
                    examplemsg.PictureBytes = System.Convert.FromBase64String(mm.Body.GetString("pictureBytes"));
 
                logger.Info("got ExampleMessage: " + examplemsg);
                return examplemsg;
            }
            catch (Exception e)
            {
                throw new MessageConversionException("Could not convert ExampleMessage to message", e);
            }
        }
 
	// called to convert a object to a JMS/NMS message
        public IMessage ToMessage(Object mesobj, ISession session)
        {
            logger.Info("toMessage()");
            ExampleMessage examplemsg = (ExampleMessage)mesobj;
            if (examplemsg == null)
            {
                throw new MessageConversionException("ExampleMessageConverter can not convert object of type " +
                        mesobj.GetType());
            }
            try
            {
                IMapMessage mm = session.CreateMapMessage();
                mm.Body.SetString("messageText", examplemsg.Messagetext);
                if(tradeRequest.PictureBytes!=null)
                    mm.Body.SetString("pictureBytes", System.Convert.ToBase64String(examplemsg.PictureBytes));
                else
                    mm.Body.SetString("pictureBytes",null);
                return mm;
 
            }
            catch (Exception e)
            {
                throw new MessageConversionException("Could not convert ExampleMessage to message", e);
            }
        }
    }
}

Now we add the ExampleMessageConverter to the NmsTemplate configuration.
Whenever a ExampleMessage is passed via the ConvertAndSend method, all the hard work is done by the NmsTemlate.
We do not have to care about connections, sessions, message conversion and all that stuff.
As long as we did the correct configuration ­čśë

<objects xmlns="http://www.springframework.net"
  xmlns:nms="http://www.springframework.net/nms">
 
   ...
 
  <!-- message converter for mapmessage <=> examplemessage -->
  <object id="ExampleMessageConverter" type="ClientApp.ExampleMessageConverter"/>
 
  <!-- template for automatic conversion -->
  <object id="NmsTemplate" type="Spring.Messaging.Nms.Core.NmsTemplate">
   ...
    <property name="MessageConverter" ref="ExampleMessageConverter" />
  </object>
 
 
   ...
 
</objects>

Receiving example will be shown in
Part 3.

Please feel free to leave any comments!

, , , , ,

2 Comments

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

Cross-language communication between a C# .NET client and a Java server application.
A (very) simple approach.

JMS is widely used, and has become a de-facto communication standard. It is also pretty easy to use and allows asynchronous processing of messages. A popular open source messaging provider is ActiveMQ. It has a very big advantage, because there is also a C# implementation available, which is part of the messaging in Spring.NET.
If your are familiar with JMS and Spring it’s very simple to understand how to use NMS and Spring.NET.

For this communication example i used Spring 3.0.0 Release, ActiveMq NMS 1.1.0 and Spring.NET 1.3.0 GA.

First step:
We need a POJO/PONO object as data container. A language independed approach is to use XML for schema description. A more improved version would be to generate the neccessary MessageConverters also, but I will drop some words about this later.

 
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <xs:element name="ExampleMessage">
      <xs:complexType>
         <xs:sequence>
            <xs:element name="Messagetext" type="xs:string"/>
            <xs:element name="ImageData" type="xs:hexBinary"/>
         </xs:sequence>
      </xs:complexType>
   </xs:element>
</xs:schema>

Our ExampleMessage encloses two fields. A string variable for the message text and a byte-array for image data.
With this XML definition its easy to generate code (C#,Java) for both application parts.

In the Microsoft world there is a command line tool named xsd.exe, which is part of the Windows SDK.

xsd.exe /c examplemessage.xsd

The output:

using System.Xml.Serialization;
 
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace="", IsNullable=false)]
public partial class ExampleMessage {
 
    private string messagetextField;
 
    private byte[] imageDataField;
 
    ///
    public string Messagetext {
        get {
            return this.messagetextField;
        }
        set {
            this.messagetextField = value;
        }
    }
 
  ...    
 
}

jaxb can be used for the Java code generation.

xjc.bat -p it.koller.interopexample examplemessage.xsd
package it.koller.interopexample;
 
public class ExampleMessage
{
     private String messagetext;
     private byte[] imagedata;
 
     ...
 
    /* getter &amp; setter methods for var access */
     public String getMessagetext()
     {
           return messagetext;
     }
 
     public void setName(String messagetext)
     {
          this.messagetext = messagetext;
     }
 
     ...
 
}

For C# client example see Part 2.

Please feel free to leave any comments!

, , , , ,

3 Comments