Catalyst::Engine::Stomp after 0.16

We use Catalyst::Engine::Stomp for all our ActiveMQ consumers. It works rather well, and (especially with the addition of our Net::ActiveMQ) makes working with queues mostly painless.

But there is always room for improvement. There were two main missing pieces:

  1. generalisation of the subscriptions and inputs, from “a queue per
    controller” to “one subscription per controller”
  2. dispatching to an action based on the JMSType header, instead
    of the ad-hoc @type body attribute we have been employing

This is the story of how I implemented both.

Generalising the inputs

Topics

The action_namespace attribute of controllers was used to derive the name of the queue to subscribe to, for messages destined for that controller. This works, until you want to subscribe to a topic.

The first step of generalisation is easy: if the namespace starts with topic/, don’t assume it’s a queue. With the addition of some nicer back-compatibility, this is the core of my first commit:
https://github.com/dakkar/catalyst-engine-stomp/commit/5d0e35bee1b8904810418be7302fd8850a2a7a37

While I was there, I also implemented a feature that was almost-documented: global (and per-broker) subscription headers.

Subscription Headers

Next, I noticed (mostly by looking harder at the STOMP protocol specification) that the module did not allow the specification of credentials for the connection to the broker. Also, the subscription headers would be much more useful if they were per-controller, not just per-broker.

So I implemented those as well: https://github.com/dakkar/catalyst-engine-stomp/commit/bf4a21663765f92b7690591e0eb07589c2f50306

The documentation I added is probably not my best piece of prose, but I sincerely could not find a clearer way of explaining the details (patches welcome!).

The important consequence is that you can now have:

  • credentials at connection
  • general subscription options
  • different controllers subscribing to the same queue (or topic) with different selectors

The last one is the most interesting one — to see why, let’s look at the test controllers TestNewStyleController.pm and TestNewStyleController2.pm.

They both get messages from the same queue, /queue/newstyle, but the first one only receives messages of type test_foo, or with custom_header: 1, while the second receives test_bar or custom_header: 2. As the test confirms, the routing works as you would expect: TestNewStyleController.pm only gets one set of messages, TestNewStyleController2.pm only gets the other set.

Why would I want to do that??

A couple of examples:

  1. your architecture is weird and you get disparate messages on the same destination, but you don’t want to rewrite half the dispatcher or have unrelated logic in the same controller
  2. your subscriptions come from a configuration file, and you want to give your users the freedom to do strange things

Dispatch on header value

The reason we dispatched on @type was that the message headers were not passed to the Catalyst dispatcher by Catalyst::Engine::Stomp. This part was rather easy to solve:
the HTTP::Request used inside Catalyst is similar enough to a STOMP frame that I was able to just shove the STOMP headers into $c->req (and then extract headers from $c->res for the optional reply).

Then, I changed Catalyst::Controller::MessageDriven to (optionally, to keep backward-compatibility) use the header value instead of the message type from the body.

Conclusions

As soon as PMOONEY merges my changes (there are some test failures, apparently, that I can’t reproduce), we’ll be able to use the full flexibility of the STOMP protocol in our Catalyst applications.

Print Friendly
This entry was posted in Architecture, Perl by dakkar. Bookmark the permalink.

About dakkar

Gianni is a Perl Architect at NAP. His code from previous lives runs in universities administration software, inside ask.com news system, and even in Antarctica. He's currently busy writing libraries to make "the right thing" be "the easy thing".

Leave a Reply