Sunday, June 24, 2007

Integrating Rails and ActiveMQ with ActiveMessaging/REST

ActiveMQ ships with a simple chat demo. The chat demo uses a Servlet to relay AJAX requests to and from a JMS Topic (the source for this application is also a good place to start if you are interested in learning about Jetty Continuations). Below are some instructions on how to build an alternate client with Rails for the chat demo. This is done via activemessaging(AKA a13g), a plugin which relays/dispatches the messages to the Servlet (or any other "broker").

Assuming Java, Ruby and Rails ...

  $rails -v
Rails 1.2.3
$ruby -v
ruby 1.8.5
$java -version
java version "1.6.0_01"

Setting Up ActiveMQ

Download and unzip the binary. Start the broker and demo application by running /apache-activemq-5.0-SNAPSHOT/bin/activemq . This only works with a 20070623 (or later) build of ActiveMQ. For an earlier build, see this patch.

Create a Rails application

  $rails a13g 
(output excluded)

Generate a Controller and a View

  $cd a13g
$script/generate controller talk index
exists app/controllers/
exists app/helpers/
create app/views/talk
exists test/functional/
create app/controllers/talk_controller.rb
create test/functional/talk_controller_test.rb
create app/helpers/talk_helper.rb
create app/views/talk/index.rhtml

Make a13g/app/controllers/talk_controller.rb look like this:

class TalkController < ApplicationController

include ActiveMessaging::MessageSender
publishes_to :chat_client

def new
@message = params[:message]
publish :chat_client, @message
render :action => 'index'


Make a13g/app/views/talk/index.rhtml look like this:
  <%= start_form_tag :action => 'new' %>
<%=text_field_tag :message %>
<%= submit_tag "submit" %>
<%= end_form_tag %>

Install the a13g plugin and it's dependencies

  $script/plugin install
(output excluded)
$gem install daemons
(output excluded)
$gem install stomp
(output excluded)

Create a Processor

  $script/generate processor ChatClient
create app/processors
create app/processors/chat_client_processor.rb
create test/functional/chat_client_processor_test.rb
create config/messaging.rb
create config/broker.yml
create app/processors/application.rb
create script/poller

An a13g Processor is similar to a MessageConsumer. Make the a13g/app/processors/chat_client_processor.rb look like this:
  class ChatClientProcessor < ApplicationProcessor

subscribes_to :chat_client

def on_message(message)
logger.debug "ChatClientProcessor received: " + message.to_s

Configuring a13g

Destinations are mapped to symbols using messaging.rb (BTW, a13g committers - if you are reading this, please consider changing Gateway.queue to Gateway.destination, for reasons demonstrated in this post ). Make a13g/config/messaging.rb look like this:
  ActiveMessaging::Gateway.define do |s|    
s.queue :chat_client, 'topic://CHAT.DEMO'

Copy this a13g adapter to a13g/vendor/plugins/activemessaging/
lib/activemessaging/adapters/rest.rb .
Tell a13g to use the adapter by making a13g/config/broker.yml look like this:
adapter: rest
port: 8161
app: "/demo/amq"
host: localhost

Start Rails and a13g

(output excluded)

  $script/poller run 
(output excluded)
Subscribing to topic://CHAT.DEMO (processed by ChatClientProcessor)
Subscribed to topic://CHAT.DEMO, using session id of 1swb9sf6yi1ke

The rest adapter establishes an HTTP session. The session id will be used for all subsequent communication (publishing and consuming).

Let's Chat

In your browser go to the chat demo and start a conversation.

Looking at the console for the activemessaging poller process, we can see that this message was received by the ChatClientProcessor.
  $script/poller run 
(output excluded)
Subscribing to topic://CHAT.DEMO (processed by ChatClientProcessor)
Subscribed to topic://CHAT.DEMO, using session id of 1swb9sf6yi1ke
ChatClientProcessor received: I was thinking ... one day,
they are going to standardize DSLs

By pointing a different browser window (different cookie/session) we can start an argument via the talk view.

Back in the original browser, we can see that chat messages are dispatched both ways.

The chat continues ...

From the poller console ...

ChatClientProcessor received: I was thinking ... one day,
they are going to standardize DSLs
ChatClientProcessor received: No way. Never.
ChatClientProcessor received: Think about it.
ChatClientProcessor received: Large conservative orgs will
not be open to it.
ChatClientProcessor received: True.
ChatClientProcessor received: And the only way they will
adopt DSLs is to go industry wide.
ChatClientProcessor received: Think JCP, WS-* ... only
for *each* industry ....
ChatClientProcessor received: Accounting, Finance,
Logistics, etc.
ChatClientProcessor received: Interesting


James Strachan said...

Great article Dennis!

Have added it to the ActiveMQ articles page...

Matt Buck said...

Great tutorial! This is exactly what I was looking for, so it's a bit weird that it was only written a few days ago. Clearly, someone is reading my thoughts...

Anonymous said...

Hi - super example! only one problem I found, in wmq.rb the "require 'wmq/wmq'" is blowing an exception. Looks to be related to Websphere MQ specifically, and not ActiveMQ, so I am at a bit of a loss as to what might be going wrong ... will try to debug and post fix if possible.



Anonymous said...

Where is the html and view codes?

vfdvgf said...

we provide a power leveling and free wow gold wow power leveling