JRuby JMS as a replacement for ActiveMessaging
I was talking to JRuby committer Ola Bini at RailsConf a few weeks ago about our ActiveMessaging solution, and he suggested we try using JRuby and JMS instead. By using JMS, we can eliminate the poller and have a truly “Event Driven” solution. So I’ve been playing around with JRuby and JMS in my spare time, with some really interesting results. In JRuby, we can implement the Java JMS interface MessageListener:
include_class “org.apache.activemq.ActiveMQConnectionFactory“
include_class “org.apache.activemq.util.ByteSequence“
include_class “org.apache.activemq.command.ActiveMQBytesMessage“
include_class “javax.jms.MessageListener“
= ‘development‘
RAILS_ROOT=File.expand_path(File.join(File.dirname(__FILE__), ‘..‘,‘..‘))
load File.join(RAILS_ROOT, ‘config‘, ‘environment.rb‘)
include javax.jms.MessageListener
message_body = serialized_messageed_message.get_content.get_data.inject(““) {|body, byte| body << byte }
customer_payload = YAML.load(message_body)
customer = Customer.new(:name => customer_payload.name)
customer.id = customer_payload.id
customer.save!
end
factory = ActiveMQConnectionFactory.new(“tcp://localhost:61616“)
connection = factory.create_connection();
session = connection.create_session(false, Session::AUTO_ACKNOWLEDGE);
queue = session.create_queue(“Customer“);
consumer = session.create_consumer(queue);
consumer.set_message_listener(self);
connection.start();
puts “Listening…“
end
end
handler = MessageHandler.new
handler.run
Contrast this with similar Java code:
{
private Connection connection;
private Session session;
private Queue queue;
{
String message = ““;
ActiveMQBytesMessage bytes_message = (ActiveMQBytesMessage)serializedMessage;
ByteSequence sequence = bytes_message.getContent();
for(int i=0; i < sequence.getData().length; i++) {
message += (char)sequence.getData()[i];
}
// Here is where you would use the message to create the Customer object
// For now I will just print the YAML
System.out.println(message);
}
{
MessageHandler handler = new MessageHandler();
handler.run();
}
{
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(“tcp://localhost:61616“);
connection = factory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
queue = session.createQueue(“Customer“);
MessageConsumer consumer = session.createConsumer(queue);
consumer.setMessageListener(this);
connection.start();
System.out.println(“Listening…“);
}
}
You’ll notice that the ‘run’ methods are identical, save for the “rubyisation” of the Java code (underscores and the like). Java requires you to implement the onMessage method when inheriting from the MessageListener class, so we have done that in JRuby.
The Java class definition:
is replaced with the JRuby equivalent:
include javax.jms.MessageListener
The rest of the JRuby code is very similar to its Java equivalent, though it was tough going back to the type-safety gymnastics of Java, I must admit.
I was surprised that with the version of JRuby I am running (ruby 1.8.5 (2007-06-02 rev 3812) [i386-jruby1.0.0RC3]), I had to use camel case for the onMessage method definition rather then ruby-like underscores, but it’s no big deal.
We’ll save this file under the processors directory of our Orders application as message_handler.rb
Then we can deploy the Orders application in JRuby. Note that this isn’t strictly necessary (but more on that later).
From the instructions on the JRuby wiki:
Install the Goldspike plugin:
~/orders]>script/plugin install svn://rubyforge.org/var/svn/jruby-extras/trunk/rails-integration/plugins/goldspike
Install the ActiveRecord-JDBC gem:
~/orders]>gem install activerecord-jdbc –no-rdoc –no-ri
The plugin provides the following rake task:
war:standalone:create - which packages up a web archive containing your application along with JRuby and the rails libraries
Run the task, and deploy the war to your app server (I’m using Tomcat)
We can then make the apache-activemq-4.1.1.jar available to our script for the jms-related code by setting the CLASSPATH environment variable:
export CLASSPATH=/path_to_jar/apache-activemqvemq-4.1.1.jar:$CLASSPATH
Then all we need to do is run the JRuby code with the following command:
jruby /path_to_tomcat/webapps/orders/processors/message_handler.rb
And that’s it. Now we can process Customer messages using our JRuby JMS implementation.