Tuesday, April 6, 2010

JRuby, JMS and Pollers

A common way in Ruby to utilize messaging is to use ActiveMessaging, and for ActiveMessaging to listen to queues/topics, etc. - implement "pollers", which will run as separate processes and listen/react to the messages.

ActiveMessaging takes these processors, and runs them in the environment (which is started externally) - creating new processes.

In trying to get everything in one box (VM), we needed a way to get around this, on top of using ActiveMessaging as a whole (since most of the app will be converted to java eventually).

In doing this, we took the poller processors and turned them into MessageListeners:
class QueueProcessor  < ApplicationProcessor
  include javax.jms.MessageListener

defined them as classes, and made sure they could run on their own and still function much like they did before:

class QueueProcessor  < ApplicationProcessor
  include javax.jms.MessageListener

  def initialize
    ...
  end

  def run
    # Instantiate a Sun Message Queue ConnectionFactory
    queueConnFactory = ConnectionFactory.new

    # Create a connection to the Sun Message Queue Message service
    queueConn = queueConnFactory.createConnection()

    # Create a session within the connection 
    queueSess = queueConn.createSession(false, Session::AUTO_ACKNOWLEDGE)

    # Instantiate a System Message Queue Destination
    # ToDo: Need to lookup via JNDI or some sort of aliasing.
    queueQueue = Queue.new("MyQueue")

    # Create a message consumer and listener
    queueMsgConsumer = queueSess.createConsumer(queueQueue)
    queueMsgConsumer.setMessageListener(self);

    # Start the Connection
    queueConn.start()
  end

  def onMessage(message)
    begin
      # Process our message as needed
      ...
    rescue
      @queue_logger.error "QueueProcessor caught #{$!} \n #{$!.backtrace.join("\n")}"
      ...
    end
  end
end

### Make sure to only run this process, and not any others.
if __FILE__ == $0
 queueProc = QueueProcessor.new
 queueProc.run
end

The next step in getting this to run separately, yet still in the same VM? I am thinking OSGi and ScriptEngine - so onto the next step to see if that is possible. :)

JRuby, JMS, OpenMQ, and Serialization

In working towards getting the current application running entirely within the Glassfish context, one of the issues I had to do was get the current implementation of the communication to the message queues away from ActiveMessaging/ActiveMQ into using JMS/OpenMQ.

There are a lot of fingers that ActiveMessaging has in here, so we will have to do some cleanup, but something that was causing me a few issues we figured out today was with the way the serializing of objects and placing in the queue as TextMessages was getting a bit out of whack when we converted some of the publishing models of ActiveMessaging to the sending of the message via JMS.

The serialization (which we are using Marshal dump/load for the ruby objects) was becoming an instance of TextMessageImpl (Java::ComSunMessagingJmqJmsclient::TextMessageImpl).

Simply pulling the text out of this on the jruby side fixed for us:
message = deserialize(message.getText())
and the deserialization worked ok.