JMS Queue Listener with Warbler

We have a rails app we’re packaging with Warbler. We have a JMS queue
listener in Ruby that we want to start listening when the app boots in
our
servlet container (Tomcat). The listener needs access to models and
other
code in the app, so it should run just like a ‘runner’ script, know it’s
RAILS_ENV, etc. What’s the best way to set this up with Warbler
nowadays?

Thanks,

Steve

On Tue, Dec 1, 2009 at 4:33 PM, Steve M. [email protected]
wrote:

We have a rails app we’re packaging with Warbler. We have a JMS queue
listener in Ruby that we want to start listening when the app boots in our
servlet container (Tomcat). The listener needs access to models and other
code in the app, so it should run just like a ‘runner’ script, know it’s
RAILS_ENV, etc. What’s the best way to set this up with Warbler nowadays?

I’m using the JMS support in JRuby-Rack, setting it up in a file
conditionally loaded by environment.rb if $servlet_context is defined
– i.e. if we’re running in the container:

require ‘jms/listener’ if $servlet_context

The relevant parts of lib/jms/listener.rb look like this:

require ‘jruby/rack/queues/activemq’

queues = […]

JRuby::Rack::Queues::ActiveMQ.configure do |mq|
mq.queues = queues
mq.url = url
end

queues.each do |q|
JRuby::Rack::Queues::Registry.register_listener(q) do |msg|
# here you’re just in Rails - handle msg however you like
end
end

This is working out pretty well for us, though I’ve run into a memory
leak doing this, documented at JRUBY-4264.

I’ve also got a few changes to JRuby-Rack to make it possible to have
the messaging bits running outside of a container in dev mode which I
need to tidy up and make available.

Hope that helps,

Chris.


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email

Thanks. I’m not using ActiveMQ; we’re using EMS (priority JMS from
TIBCO).
Would I use ‘local’ instead of ‘activemq’?

Steve

The default approach where you just get your generic JMS connection
from JNDI should involve the following steps:

  1. Add a context listener in web.xml:
    org.jruby.rack.jms.QueueContextListener
  2. Add a context init parameter in web.xml named
    ‘jms.connection.factory’ whose value is the JNDI location of the JMS
    connection factory.
  3. Register your listeners by making sure classes/models are loaded
    during initialization. In the example below, “MyQ” is the name of the
    JNDI queue or topic.

class SomeListener
extend JRuby::Rack::Queues::MessageSubscriber
subscribes_to “MyQ” do |msg|
self.new.dispatch msg
end

def dispatch(message)
# process message here
end
end

BTW, apologies for all of this not being documented. If anyone has a
moment to blog about their setup or dump some info in
http://jruby-rack.kenai.com/pages that would be great.

/Nick

On Tue, Dec 1, 2009 at 12:55 PM, Steve M. [email protected]
wrote:

our
require ‘jms/listener’ if $servlet_context
end
I’ve also got a few changes to JRuby-Rack to make it possible to have
http://xircles.codehaus.org/manage_email


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email

Thanks for the quick reply. Pardon my ignorance, but how does one
usually
go about getting a JMS connection factory into the JNDI tree?

Thanks,

Steev

Depends on the app server. For Glassfish, you can use the admin
console to create it along with the queues. If you’re using something
like an external ActiveMQ and you don’t want to use the app server’s
built-in JNDI tree, you can pass specific JNDI properties to the
jruby-rack queues code by adding a context init param named
“jms.jndi.properties” containing a chunk of a Java properties-file
(such as what’s shown in
http://activemq.apache.org/jndi-support.html).

/Nick

On Wed, Dec 2, 2009 at 10:27 AM, Steve M. [email protected]
wrote:

  1. Add a context listener in web.xml:
    subscribes_to “MyQ” do |msg|
    http://jruby-rack.kenai.com/pages that would be great.

On Tue, Dec 1, 2009 at 12:06 PM, Chris A. [email protected] wrote:

code in the app, so it should run just like a ‘runner’ script, know
The relevant parts of lib/jms/listener.rb look like this:
queues.each do |q|
need to tidy up and make available.


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email

Never mind - I just had to put the QueueContextListener before the
RailsServletContextListener in web.xml.

Steve

Thanks. I got the JNDI properties for my server straightened out. My
only
problem now is when to invoke the “subscribes_to” code. If I put your
SomeListener class (changing the queue name) at the bottom of
config/environment.rb in my rails app it blows up when I boot in tomcat:

org.jruby.rack.RackInitializationException: You have a nil object when
you
didn’t expect it!
The error occurred while evaluating nil.listen
from
file:/Users/steve/src/apache-tomcat-6.0.20/webapps/remittanceAdvice/WEB-INF/lib/jruby-rack-0.9.6-SNAPSHOT.jar!/jruby/rack/queues/message_subscriber.rb:43:in
`subscribes_to’

This seems to happen because the queue manager has not yet been placed
in
the servlet context attribute rack.queue.manager
((Java::OrgJrubyRackJms::QueueManager::MGR_KEY). I guess this is
because
QueueContextListener.contextInitialized has not been called yet? I’m
not
see the log message "“Error initializing queue manager” that would get
logged if there was an exception creating the queue manager.

If I instead move SomeListener out of environment.rb and stick it in a
controller, wait for the app to boot, and then hit the controller with a
web
request to trigger it and the ‘subcribes_to’ line to get evaluated
everything is hunky dory. I can see with probe that there is an object
in
“rack.queue.manager”, and my listener starts processing messages.

Do I need to change something in web.xml to force the listener to get
initialized earlier?

Thanks,

Steve

On Thu, Dec 3, 2009 at 5:06 PM, Steve M. [email protected]
wrote:

OK everything’s working thanks for all the help. Still have a few more
question though :wink:

  1. How can I vary the JNDI JMS properties by RAILS_ENV?

And still use the same war file? We’d need an extra layer of
indirection between the context init property and the queue manager,
or possibly move the config out to a config/queues.yml or similar. I
can’t think of a way to accomplish this without changing the code.

  1. We have some extra security in our JMS production environment that
    requires us to pass the user and password when creating the connection,
    using the overloaded version of connectionFactory.createConnection(user,
    pass). It doesn’t look like the current jruby-rack JMS stuff supports
    this. Would it make sense for me to make a patch for
    DefaultQueueManager.listen that would pass the and a user and password to
    connectionFactory.createConnection if some special properties were set in
    jms.jndi.properties?

Sure, sounds good.

/Nick


To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email

OK everything’s working thanks for all the help. Still have a few more
question though :wink:

  1. How can I vary the JNDI JMS properties by RAILS_ENV?

  2. We have some extra security in our JMS production environment that
    requires us to pass the user and password when creating the connection,
    using the overloaded version of connectionFactory.createConnection(user,
    pass). It doesn’t look like the current jruby-rack JMS stuff supports
    this. Would it make sense for me to make a patch for
    DefaultQueueManager.listen that would pass the and a user and password
    to
    connectionFactory.createConnection if some special properties were set
    in
    jms.jndi.properties?

STeve