Chapter 3. Sample Applications

3.1. Introduction

The Spring AMQP project includes two sample applications. The first is a simple "Hello World" example that demonstrates both synchronous and asynchronous message reception. It provides an excellent starting point for acquiring an understanding of the essential components. The second sample is based on a simplified stock-trading use case to demonstrate the types of interaction that would be common in real world applications. In this chapter, we will provide a quick walk-through of each sample so that you can focus on the most important components. The samples are available in the distribution in the main solution file.

3.2. Hello World

The Hello World sample demonstrates both synchronous and asynchronous message reception.

3.2.1. Synchronous Example

Within the HelloWorld solution folder navigate to the Spring.Amqp.HelloWorld.BrokerConfiguration class. Run the "Program.cs" main application there in order to create a new queue declartion named "hello.world.queue" on the broker.

The HelloWorld/Sync solution folder has a project named Spring.Amqp.HelloWorldProducer. The Spring XML configuration for creating the RabbitTemplate instance is shown below

<objects xmlns="http://www.springframework.net">

  <object id="ConnectionFactory" type="Spring.Messaging.Amqp.Rabbit.Connection.SingleConnectionFactory, Spring.Messaging.Amqp.Rabbit">
    
  </object>

  <object id="RabbitTemplate" type="Spring.Messaging.Amqp.Rabbit.Core.RabbitTemplate, Spring.Messaging.Amqp.Rabbit">
    <constructor-arg ref="ConnectionFactory"/>
    <!-- The queue will be bound to the default direct exchange unless specified otherwise -->
    <property name="Queue" value="hello.world.queue"/>
    <property name="RoutingKey" value="hello.world.queue"/>
  </object>

</objects>

This is identical to the configuration of the Consumer application.

Looking back at the "rabbitTemplate" object definition configuration, you will see that it has the helloWorldQueue's name set as its "queue" property (for receiving Messages) and for its "routingKey" property (for sending Messages).

Now that we've explored the configuration, let's look at the code that actually uses these components. First, open the Program.cs file in the Producer project It contains a main() method where the Spring ApplicationContext is created.

        static void Main(string[] args)
        {
            using (IApplicationContext ctx = ContextRegistry.GetContext())
            {
                IAmqpTemplate amqpTemplate = (IAmqpTemplate) ctx.GetObject("RabbitTemplate");
                log.Info("Sending hello world message.");
                amqpTemplate.ConvertAndSend("Hello World");
                log.Info("Hello world message sent.");                
            }

            Console.WriteLine("Press 'enter' to exit.");
            Console.ReadLine();
        }

As you can see in the example above, an instance of the IAmqpTemplate interface is retrieved and used for sending a Message. Since the client code should rely on interfaces whenever possible, the type is IAmqpTemplate rather than RabbitTemplate. Even though this is just a simple example, relying on the interface means that this code is more portable (the configuration can be changed independently of the code). Since the ConvertAndSend() method is invoked, the template will be delegating to its IMessageConverter instance. In this case, it's using the default SimpleMessageConverter, but a different implementation could be provided to the "rabbitTemplate" bean as defined in HelloWorldConfiguration.

Now open the Consumer project. It actually shares the same configuration as the producer project. The Consumer code is basically a mirror image of the Producer, calling ReceiveAndConvert() rather than ConvertAndSend().

        static void Main(string[] args)
        {
            using (IApplicationContext ctx = ContextRegistry.GetContext())
            {
                IAmqpTemplate amqpTemplate = (IAmqpTemplate)ctx.GetObject("RabbitTemplate");
                log.Info("Synchronous pull");
                String message = (String) amqpTemplate.ReceiveAndConvert();
                if (message == null)
                {
                    log.Info("[No message present on queue to receive.]");
                }
                else
                {
                    log.Info("Received: " + message);
                }
            }

            Console.WriteLine("Press 'enter' to exit.");
            Console.ReadLine();
        }

If you run the Producer, and then run the Consumer, you should see the message "Received: Hello World" in the console output.

3.2.2. Asynchronous Example

Now that we've walked through the synchronous Hello World sample, it's time to move on to a slightly more advanced but significantly more powerful option. With a few modifications, the Hello World sample can provide an example of asynchronous reception, a.k.a. Message-driven POCOs. In fact, there is a proejct that provides exactly that in HelloWorld/Async solution folder.

Once again, we will start with the sending side. Open the ProducerConfiguration class and notice that it creates a "connectionFactory" and "rabbitTemplate" object definition. Recall that messages are sent to an Exchange rather than being sent directly to a Queue. The AMQP default Exchange is a direct Exchange with no name. All Queues are bound to that default Exchange with their name as the routing key. That is why we only need to provide the routing key here.

<objects xmlns="http://www.springframework.net">

  <object id="ConnectionFactory" type="Spring.Messaging.Amqp.Rabbit.Connection.SingleConnectionFactory, Spring.Messaging.Amqp.Rabbit">

  </object>

  <object id="RabbitTemplate" type="Spring.Messaging.Amqp.Rabbit.Core.RabbitTemplate, Spring.Messaging.Amqp.Rabbit">
    <constructor-arg ref="ConnectionFactory"/>
    <!-- The queue will be bound to the default direct exchange unless specified otherwise -->
    <property name="RoutingKey" value="hello.world.queue"/>
  </object>

</objects>

Since this sample will be demonstrating asynchronous message reception, the producing side is designed to continuously send messages (if it were a message-per-execution model like the synchronous version, it would not be quite so obvious that it is in fact a message-driven consumer).

    class Program
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(Program));

        static void Main(string[] args)
        {
            using (IApplicationContext ctx = ContextRegistry.GetContext())
            {
                IAmqpTemplate amqpTemplate = (IAmqpTemplate)ctx.GetObject("RabbitTemplate");
                int i = 0;
                while (true)
                {
                    amqpTemplate.ConvertAndSend("Hello World " + i++);
                    log.Info("Hello world message sent.");
                    Thread.Sleep(3000);
                }
            }            
        }
    }

Now, let's turn to the receiving side. To emphasize the Message-driven POCO behavior will start with the component that is reacting to the messages. The class is called HelloWorldHandler.

    public class HelloWorldHandler
    {
        public void HandleMessage(string text)
        {
            Console.WriteLine("Received: " + text);
        }
    }

Clearly, that is a POCO. It does not extend any base class, it doesn't implement any interfaces, and it doesn't even contain any imports. It is being "adapted" to the MessageListener interface by the Spring AMQP MessageListenerAdapter. That adapter can then be configured on a SimpleMessageListenerContainer. For this sample, the container is created in the Application.xml configuration file. You can see the POCO declared there.

<objects xmlns="http://www.springframework.net">

  <object id="ConnectionFactory" type="Spring.Messaging.Amqp.Rabbit.Connection.SingleConnectionFactory, Spring.Messaging.Amqp.Rabbit">

  </object>

  <object id="MessageListenerContainer" type="Spring.Messaging.Amqp.Rabbit.Listener.SimpleMessageListenerContainer, Spring.Messaging.Amqp.Rabbit">
    <property name="ConnectionFactory" ref="ConnectionFactory"/>
    <property name="Queue" value="hello.world.queue"/>
    <property name="ConcurrentConsumers" value="5"/>
    <property name="MessageListener" ref="MessageListenerAdapter"/>
  </object>


  <object id="MessageListenerAdapter" type="Spring.Messaging.Amqp.Rabbit.Listener.Adapter.MessageListenerAdapter, Spring.Messaging.Amqp.Rabbit">
    <property name="HandlerObject" ref="HelloWorldHandler"/>
  </object>

  <object id="HelloWorldHandler" type="Spring.Amqp.HelloWorld.Consumer.Async.HelloWorldHandler, Spring.Amqp.HelloWorld.Consumer.Async">
    
  </object>


</objects>

You can start the Producer and Consumer in any order, and you should see messages being sent and received every 3 seconds.

To run the application make sure that you select the properties of the top level solution and select "Multiple Startup Project" option. Pick the producer and consumer applications"

3.3. Stock Trading

TODO