BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles Introduction to ActiveMessaging for Rails

Introduction to ActiveMessaging for Rails

This item in japanese

Bookmarks

Introduction

Rails has already planted its flag firmly in the ground of database driven web applications. ActiveMessaging is a Rails plugin framework that extends the borders of Rails territory by adding simplified messaging integration. With ActiveMessaging and Rails now you can loosely integrate with systems as disparate as mainframes sending MQ messages or J2EE webapps, offload processing long-running tasks, or create applications with event or message-driven architectures. As others have put it, ActiveMessaging is trying to do for messaging what ActiveRecord does for databases. This article will introduce you to ActiveMessaging and related technologies, and get you started using it in your Rails applications now.

Messaging Standards and Brokers

Three things you need to get messaging working: a communication protocol, a broker speaking this protocol, and a client library for your applications speak the protocol. Technically, the fourth thing is a system to send messages to, but in this example we'll create a system that's a bit pathetic and talks to itself. For the protocol, ActiveMessaging supports Stomp: Streaming Text-Oriented Messaging Protocol. Stomp is a messaging standard to exchange text based messages over wire connections, usually tcp/ip. The idea was to make a protocol simple enough that clients and servers for it can be created easily in almost any language (e.g. the Ruby Stomp client is about 400 lines, soaking wet with comments and blank lines), so it should be possible to integrate almost any system.

The Apache ActiveMQ message broker supports Stomp natively, though other broker options are available, with more coming all the time. In fact, the recently announced StompConnect project makes any JMS broker into a Stomp broker, which opens up Stomp, and ActiveMessaging, to practically every open source and proprietary messaging product. On the client side, ActiveMessaging supports protocols using an adapter pattern, and for Stomp it extends the Stomp RubyGem to be that adapter. More adapters are possible and in the works, but Stomp is the only one supported today.

For this article, I'll be using ActiveMQ as the broker, and working with the as yet unreleased 4.2 branch. ActiveMQ has install instructions for all platforms, but here's a simple procedure that assumes you have Java 1.4 or later:

cd /usr/local/src
#unix and cygwin
wget http://people.apache.org/repo/m2-snapshot-repository/org/apache/activemq/apache-activemq/4.2-SNAPSHOT/apache-activemq-4.2-20070221.081507-10-src.tar.gz
#os x
curl -O http://people.apache.org/repo/m2-snapshot-repository/org/apache/activemq/apache-activemq/4.2-SNAPSHOT/apache-activemq-4.2-20070221.081507-10-src.tar.gz

cd ..
tar xvfz apache-activemq-4.2-20070221.081507-10-src.tar.gz
cd apache-activemq-4.2-incubator-SNAPSHOT
./bin/activemq

That's it! By default, ActiveMQ's Stomp support should be configured to start up automatically, but if not you should add the following line to conf/activemq.xml:

<transportConnectors> 
...
<!-- Add this line -->
<transportConnector name="stomp" uri="stomp://localhost:61613"/>
</transportConnectors>

That's the minimum, to configure ActiveMQ to your heart's desire, check out the ActiveMessaging wiki page on ActiveMQ, and the using ActiveMQ guides.

Rails Setup

Assuming you have Rails 1.1+ and MySql already, you'll also need 2 more RubyGems: daemons and Stomp:

sudo gem install daemons
sudo gem install stomp

Let's start with a newly minted Rails 'MessageMonster', a database for it, and install the ActiveMessaging plugin:

cd /usr/local/dev
rails MessageMonster
mysqladmin create messagemonster_development -u root
cd MessageMonster
script/plugin install http://activemessaging.googlecode.com/svn/trunk/plugins/activemessaging

All the pre-requisites are in place, so it's time to write an application.

Processing Messages

We'll start by creating a 'processor', which in ActiveMessaging lingo is a class that subscribes to destinations in order to receive and act on messages. It is the message driven equivalent of your controllers, and along with your model where you'll put most of your code.

As Rails has taught you to expect, there is a generator to create processors. Let's create one that saves messages it receives to show ActiveRecord integration:

script/generate processor PersistMessage 

Running this generate for the first time outputs the following:

create  app/processors
create app/processors/persist_message_processor.rb
create config/messaging.rb
create config/broker.yml
create app/processors/application.rb
create script/poller

Let's talk about each of these a moment.
  • app/processors is the directory for all the processors.
  • app/processors/persist_message_processor.rb is the new processor, with a default implementation for you to work from.
  • app/processors/application.rb is the common super class from which processors extend. Use this to put shared code, such as error handling.
  • config/broker.yml configures how you connect to your message broker in each environment. It's the equivalent of database.yml, but for message brokers.
  • config/messaging.rb is used for the rest of the messaging configuration, like destinations and message headers.
  • script/poller is the daemon used by ActiveMessaging to listen for messages. More on this script later.

Let's check out the broker.yml file first, to see the options and make sure the development broker configuration is right:

development:
adapter: stomp
login: ""
passcode: ""
host: localhost
port: 61613
reliable: true
reconnectDelay: 5
...

All of these values are exactly what we need for our default ActiveMQ installation, but for production you will likely have a user and password, and if you run your broker remotely, a different host and port.

So now let's look at the persist_message_processor.rb:

class PersistMessageProcessor < ApplicationProcessor
subscribes_to :persist_message
def on_message(message)
logger.debug "PersistMessageProcessor received: " + message
end
end

By default you'll see it is subscribed to :persist_message and has one method, on_message, which will be invoked by ActiveMessaging when messages for the :persist_message subscription arrive. The body of the message is passed in as the string argument message, but if you need to get to the Stomp message object itself, it is available in the @message processor attribute. Processors can subscribe to more than one queue, simply add anther call to subscribes_to.

The :persist_message symbol is a logical name for the destination to which this processor subscribes, the definition of this destination was generated in config/messaging.rb:

ActiveMessaging::Gateway.define do |s|
s.queue :persist_message, '/queue/PersistMessage'
end

To use a comparison to Rails, messaging.rb is most like routes.rb. It allows you to map a logical name (:persist_message) to a destination on the broker (/queue/PersistMessage). Though there is much more that can be configured here, the generated default is fine for this example.

Now we'll teach the processor to save its messages for a rainy day, starting with creating a model for the messages.

script/generate model message 

Now update 'db/migrate/001_create_messages.rb' with 2 additional columns:

create_table :messages do |t|
t.column :body, :text
t.column :received_date, :datetime
end

Call migrate to create the table...

rake db:migrate 

...and on to updating the processor on_message method to use the new Message model:

def on_message(message)
logger.debug "PersistMessageProcessor received: " + message
my_message = Message.create(:body=>message, :received_date=>DateTime.new)
end

Now, you're looking at this and thinking that's nothing new - this is just how you would write the same thing in a Rails controller. That's the point, when you use ActiveMessaging, all the convenience of the Rails application environment is available to you.

With a processor to catch them, it's time to start throwing messages (bottles sold separately).

Sending Messages

To send messages we'll use a Rails view and controller in the same application:

script/generate controller SendMessage index

Edit the view to submit message text, and to show a refreshable list of previously persisted messages:

<p style="color: green"><%= flash[:notice] %></p> <h1>Send Message</h1> <%= start_form_tag :action => 'new' %> <label>Message</label> <%=text_field_tag :message, @message %>  <%= submit_tag "submit" %> <%= end_form_tag %> <%= link_to "Refresh List", :action => "index" %> <table> <% @messages.each do |m| -%>   <tr><td><%= m.received_date %></td><td><%= m.body %></td></tr> <% end %> </table> 

Edit the controller to send the message. To make sending from any class easier, ActiveMessaging has a mixin MessageSender that provides a simple publish method. The publishes_to method declares to which destination a class intends to send, but does nothing more than validate that the destination is in config/messaging.rb. Here is how the controller uses these methods:

require 'activemessaging/processor'

class SendMessageController < ApplicationController

include ActiveMessaging::MessageSender
publishes_to :persist_message

def index
@messages = Message.find :all
end

def new
@message = params[:message]
publish :persist_message, @message
flash[:notice] = "'#{@message}' sent"
@messages = Message.find :all
render :action => 'index'
end
end

And that, as they say, is that. The controller will send a message on submit, and when next you view or refresh the page you'll see the messages saved by the processor.

Running the Example

ActiveMessaging runs in its own process, controlled by the script/poller, which can be a gotcha – just starting the Rails server is not enough if you want to receive messages, and messages are received into a different running process than Rails. To run this example, we need 3 processes: the ActiveMQ broker, the Rails server running to send the message, and the script/poller to receive it, each running in their own terminal:

Start ActiveMQ:

cd /usr/local/apache-activemq-4.2-incubator-SNAPSHOT
./bin/activemq

Start Rails:

cd /usr/local/dev/MessageMonster
script/server

Run ActiveMessaging:

cd /usr/local/dev/MessageMonster
script/poller run

The script/poller follows the usual daemon semantics. Besides 'run' which keeps the processing in the foreground, you can also pass it 'start','stop','restart' and 'status'. It also uses allows you to run multiple instances simultaneously, a useful for handling higher volumes of messages.

Now navigate to the test page (i.e. http://localhost:3000/send_message ) and enter a message such as the canonical "Hello World". When the message is sent you should see the following in the poller window:

Loading /usr/local/dev/MessageMonster/app/processors/application.rb
Loading /usr/local/dev/MessageMonster/app/processors/persist_message_processor.rb
=> Subscribing to /queue/PersistMessage (processed by PersistMessageProcessor)
=> All subscribed, now polling
PersistMessageProcessor received: Hello World

The poller always shows what processors it loads on start-up, and what subscriptions requests on the broker. If the broker is unavailable or lost, it will try to reconnect based on your broker.yml configuration. After the poller start output, you can now also see the ActiveMessaging processor has received the message sent from Rails. Likewise, if you use the 'Refresh List' button, you'll see the message in the Rails view once it is persisted by the ActiveMessaging processor.

Where to go from here

I hope you enjoyed this intro to ActiveMessaging, hopefully you can see both its potential and ease of use. ActiveMessaging has more advanced capabilities not covered in this article, such as improved exception handling, grouping processors to be run by multiple pollers, or how to use Stomp and ActiveMQ message headers for tricks like synchronous messaging, JMS integration, and selective subscriptions. If you are thinking after seeing the basics that ActiveMessaging is too limited for your needs, I think you might be pleasantly surprised.

  • For more information, I recommend perusing the ActiveMessaging project site, especially the wiki and the code.
  • We are constantly improving ActiveMessaging, so to both stay up on changes (and recommend or submit your own) read or join the mailing list.
  • Thanks for your attention, and happy messaging!


    Andrew Kuklewicz has worked in software development for the last 10 years, and is senior web developer at PRX (the Public Radio Exchange), a nonprofit service for distribution, peer review, and licensing of radio pieces. When not working in public media, or tap dancing, he is the current maintainer of ActiveMessaging, a committer on the Ruby Stomp project, and frequent contributer to other Ruby on Rails, Plone, and Java open source projects.

    Rate this Article

    Adoption
    Style

    BT