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.

3 comments:
What about deployment? Running a separate JVM is quite expensive, significantly more expensive than running a separate Ruby process.
In Mingle we use the RailsTaskServlet to start processes inside the app server itself. You would need to configure it manually inside web.xml and point it to what script to start.
If you retrieve the JMS connection factory from the ap server through JNDI you also benefit from the resource management (pooling and what have you) of the app server itself.
Hi! I've been trying this piece of code with ActiveMQ 5.0 . Is there anything special for us to do specify the wireFormat as stomp? I'm trying to send messages to a JRuby consumer [using the code you have given] and the Ruby Stomp gem to send messages to it.
But somehow, the onMessage method does not get called. I am however able to receive messages on the ruby stomp gem.
Thanks a lot for all the great work!
Arun
I'm having the same problem as Arun. Any help?
Post a Comment