A JMS C# Topic Subscriber

Introduction

This example demonstrates how a .NET application written in C# can asynchronously receive messages that were published to a JMS Topic.

In this example, we're set up to receive stock quotes in two different message types: a TextMessage and a MapMessage. We use two different message types solely for the purpose of illustrating something other than TextMessages. In a real production application you would probably use one message type and save yourself message type checks and conditional casts.

The example relies on JuggerNET-generated .NET bindings for the JMS API. In the JMS Courier product, the JMS.NET bindings are bundled into an easy-to-use .NET assembly that does not require you to use the code generator. Please also see the corresponding C# Topic Publisher example as well as the platform-portable C++ examples.

The source code

The following block contains the full application except for the Example.InitJvm() procedure, which parses command line arguments and sets up some system properties based on command line input and a configuration file.

using java.lang;
using javax.naming;
using javax.jms;
using Codemesh.JuggerNET;
using System;
using System.Threading;


/// <summary>
/// The asynchronous message listener which we'll register with
/// the topic.
/// </summary>
public class QuoteListener : MessageListener
{
    string[]        symbols = null;
    Class           clsTextMessage = null;
    Class           clsMapMessage = null;

    /// <summary>
    /// Create a new listener for a certain set of symbols.
    /// </summary>
    /// <param name="symbols">the stock symbols we're listening for.</param>
    public QuoteListener( string[] symbols )
    {
        this.symbols = symbols;
        // cache the two message types we're handling
        clsMapMessage = Class.forName("javax.jms.MapMessage");
        clsTextMessage = Class.forName("javax.jms.TextMessage");
    }

    /// <summary>
    /// Process an asynchronous notification about a received message.
    /// </summary>
    /// <param name="message"></param>
    void  MessageListener.onMessage(Message message)
    {
        if( clsTextMessage.isInstance( message ) )
        {
            string  symbol = message.getStringProperty( "SYMBOL" );

            foreach( string sym in symbols )
            {
                if( sym.Equals( symbol ) )
                    Console.WriteLine( "{0}={1}", symbol, TextMessageImpl.From( message ).getText() );
            }
        }
        else if( clsMapMessage.isInstance( message ) )
        {
            MapMessage  mmsg = MapMessageImpl.From( message );

            foreach( string sym in symbols )
            {
                if (mmsg.itemExists( sym ) )
                    Console.WriteLine("{0}={1}", sym, mmsg.getDouble( sym ) );
            }
        }
    }
}


///
/// @brief The quote subscriber example
///
/// This is a heavily documented example that illustrates how to
/// subscribe to stock quotes published via a JMS topic and the 
/// JMS Courier .NET APIs.
///
public class Application
{
    public static void Main(string[] args)
    {
        if( args.Length < 1 )
        {
            Console.Error.WriteLine( "Usage: quotesub [tickersymbol]+" );
            Console.Error.WriteLine( "Usage: quotesub CDMSH AAPL MSFT" );
            Environment.Exit( 1 );
        }

        try
        {
            // set up the Java runtime environment (defined in 'Example.cs')
            //
            // This function has to at least initialize
            // - the classpath, 
            // - the JVM path (unless you wish to rely on a default JVM we might or might not find),
            // - the JNDI properties
            // - lookup names
            //
            // We also set up the JMS JNDI properties in this method.
            //
            // You can perform these steps in code or rely on a configuration file,
            // it's up to you.  All these cases are documented in the 'rtconfig' examples.
            //
            Example.InitJvm( args );

            // the JNDI properties are assumed to be set up correctly at this point;
            // That all happens in the InitJvm() function, WHICH IS EXAMPLE CODE AND
            // DOES NOT MEAN THAT YOU HAVE TO DO THINGS THIS WAY!
            InitialContext            ictx = new InitialContext();

            // The .NET examples assume that the TopicConnectionFactory lookup name is in the system
            // properties at this point.  That all happens in the InitJvm() function, WHICH IS 
            // EXAMPLE CODE AND DOES NOT MEAN THAT YOU HAVE TO DO THINGS THIS WAY!
            // We look for the property "TCF" and give it the default value "TopicConnectionFactory" 
            // if we can't find it.
            string                    tcfName = java.lang.System.getProperty( "TCF", "TopicConnectionFactory" );
            TopicConnectionFactory    tcf = TopicConnectionFactoryImpl.From( ictx.lookup( tcfName ) );
        
            if ( tcf == null )
            {
                Console.Error.WriteLine( "TCF not found for lookup name '{0}'", tcfName );
                Environment.Exit( -1 );              
            }

            // create a topic connection
            // topic connections are relatively heavy objects, so don't create more than one
            // unless you know that you really have to.  It's better to work with multiple 
            // sessions instead.
            TopicConnection            tc = tcf.createTopicConnection();

            // create a session in which we publish (non-transacted, auto-acknowledging)
            // a session should never be shared by more than one thread. If you have more than
            // one thread you need more than one session.
            TopicSession            ts = tc.createTopicSession( false, SessionImpl.AUTO_ACKNOWLEDGE );

            // look up the topic name we want to use (set up in 'InitJvm')
            string                    topicName = java.lang.System.getProperty( "TOPIC", "testTopic" );
            Topic                    topic = TopicImpl.From( ictx.lookup( topicName ) );

            // create the publisher for that topic
            TopicSubscriber            tsub = ts.createSubscriber( topic );

            Console.WriteLine( "Starting to listen for quotes on topic '{0}'", topicName );
            Console.WriteLine( "Hit <ENTER> to quit..." );

            // set up the ticker symbols and quotes
            String[]                symbols = args;

            tsub.setMessageListener( new QuoteListener(symbols) );
            tc.start();

            while( !NativeInterface.Kbhit() )
                Thread.Sleep( new TimeSpan( 1000000 ) );

            // orderly shutdown processing
            tc.stop();
            tsub.setMessageListener(null);
            ts.close();
            tc.close();

            Console.WriteLine( "Done publishing!" );

            return;
        }
        // you can write hierarchical exception catch blocks, just like in Java
        // and you have full access to the entire exception information and
        // functionality
        catch( NamingException nex )
        {
            Console.Error.WriteLine( "JNDI Naming Exception: {0}", nex.ToString() );
            
            Environment.Exit( -1 );
        }
        catch( System.Exception e )
        {
            Console.Error.WriteLine( "Exception: {0}", e.ToString() );
            
            Environment.Exit( -1 );
        }
    }
}


Copyright 2006-2011 by Codemesh, Inc., ALL RIGHTS RESERVED

:
jms c# topic subscriber
home products support customers partners newsroom about us contact us