STOMP adapter updated for RabbitMQ 1.3.0

(This page is a mirrored copy of an article originally posted on the LShift blog; see the archive index here.)

I’ve updated our STOMP adapter for RabbitMQ to fix a bug reported by Carl Bourne. In the process, I updated the code to work with the latest snapshots of RabbitMQ, including the currently-released version, v1.3.0.

You can get the code by checking it out from our repository with

hg clone http://hg.rabbitmq.com/rabbitmq-stomp/
hg update rabbitmq_v1_3_0_branch

UPDATE: use the default branch these days, unless you’re still running 1.3.0!

or you can instead download a snapshot of the current state of the adapter[1], currently at revision 90dd1726fe0b.

(Update: I forgot to mention that the mercurial repository has two branches in it: default, which tracks our internal RabbitMQ server repository, and rabbitmq_v1_3_0_branch, which should stay compatible with the 1.3.0 server release. Thanks to Aman Gupta, who pointed out the problem in a comment below!)

Here’s a summary of how to build and run a STOMP-enabled RabbitMQ broker - for more details, see the original post on the topic:

  1. First, retrieve the RabbitMQ server 1.3.0 source code, and unpack it:
    curl http://www.rabbitmq.com/releases/source/rabbitmq-1.3.0.tar.gz | tar -zxvf -

  2. Next, grab the latest STOMP adapter (here we download a copy of the rabbitmq_v1_3_0_branch rather than the main trunk):
    curl http://hg.rabbitmq.com/rabbitmq-stomp/archive/rabbitmq_v1_3_0_branch.tar.gz | tar -zxvf -

  3. Compile the server itself:
    make -C rabbitmq-1.3.0/erlang/rabbit

  4. Finally, compile the adapter, and start the server with extra options that cause the adapter to start too:
    make -C rabbitmq-stomp-rabbitmq_v1_3_0_branch run

If this is successful, you should end up with “starting STOMP-listeners …done” and “broker running” in your terminal. At this point you can try out the service - for instance, you can run Carl’s test cases if you have ruby and rubygems handy:

sudo apt-get install ruby
sudo apt-get install rubygems
sudo gem install stomp
ruby rabbitmq-stomp-rabbitmq_v1_3_0_branch/priv/tests-ruby/cb-receiver.rb

and in another window

ruby rabbitmq-stomp-rabbitmq_v1_3_0_branch/priv/tests-ruby/cb-sender.rb

It will transfer 10,000 short messages, and end up displaying

...
Test Message number 9998
Test Message number 9999
All Done!

in the receiver-side terminal.

If you’re interested in the gory details of the bug-fix itself, you can see the relevant patch here. The problem was that the code that handled abrupt socket closure wasn’t handshaking with enough of the internals of the server to ensure that the last few work items were being processed successfully. Trapping socket closure in the STOMP adapter code, and politely handshaking, turned out to be all that was required. An alternative workaround would be to use STOMP’s DISCONNECT method before closing the socket on the client side.


Footnote 1: Note that despite the misleading URL, the snapshot download really is of the STOMP adapter, and not of the broker itself! I’m making use of hgwebdir’s archive-download feature here.

Comments

On 23 May, 2008 at 9:52 pm, manuel wrote:

Hello,

I’m trying to run the adapter, but it’s crashing:


Erlang (BEAM) emulator version 5.5.5 [source] [64-bit] [async-threads:30] [kernel-poll:true]

Eshell V5.5.5 (abort with ^G)
(rabbit@pennylane)1> {”init terminating in doboot”,{undef,[{mnesia,systeminfo,[directory]},{rabbitmnesia,ensuremnesiadir,0},{rabbit,start,0},{init,startit,1},{init,start_em,1}]}}

Crash dump was written to: erlcrash.dump
init terminating in do
boot ()
make[1]: *** [run] Error 1

Thank you!

On 24 May, 2008 at 1:09 am, tonyg wrote:

Hi Manuel,

Are you sure you have a full Erlang installation, including Mnesia, available? The error message you’re getting seems to indicate it cannot find the mnesia:system_info function. Some operating systems package the OTP support libraries separately from the core Erlang runtime, so perhaps there is an additional package you could install.

On 3 July, 2008 at 5:40 pm, tonyg wrote:

((I accidentally deleted a bunch of comments I didn’t mean to delete today, so I’m having to repost them manually:))

manuel said:

Hey Tony,

That was it. There was one missing pkg (’erlang’ in Ubuntu Hardy) from my erlang installation. Thanks a lot!!

On 3 July, 2008 at 5:48 pm, tonyg wrote:

((I accidentally deleted a bunch of comments I didn’t mean to delete today, so I’m having to repost them manually:))

Aman Gupta wrote:

Just ran into a bug with the latest hg stomp adapter:

{”init terminating in doboot”,{{nocatch,{error,{cannotstartapplication,rabbit,{badreturn,{{rabbit,start,[normal,[]]},{’EXIT’,{{badmatch,{ok,{0,0,0,0},’rabbitstomplistenersup0.0.0.0:61613′}},[{rabbitstomp,startlisteners,1},{rabbitstomp,start,1},{rabbitstomp,kickstart,0},{rabbit,’-start/2-fun-0-’,1},{lists,foreach,2},{rabbit,start,2},{applicationmaster,startitold,4}]}}}}}}},[{init,startit,1},{init,start_em,1}]}}

Here’s a diff that fixes it:

diff -r 538381ba2feb src/rabbitstomp.erl
— a/src/rabbitstomp.erl Mon Jun 16 16:39:29 2008 +0100
+++ b/src/rabbitstomp.erl Wed Jun 25 14:34:13 2008 -0700
@@ -57,7 +57,7 @@
startlisteners([]) ->
ok;
startlisteners([{Host, Port} | More]) ->
- {IPAddress, Name} = rabbitnetworking:checktcplisteneraddress(rabbitstomplistenersup,
+ {ok, IPAddress, Name} = rabbitnetworking:checktcplisteneraddress(rabbitstomplistenersup,
Host,
Port),
{ok,} = supervisor:start_child(

On 3 July, 2008 at 5:49 pm, tonyg wrote:

((I accidentally deleted a bunch of comments I didn’t mean to delete today, so I’m having to repost them manually:))

tonyg wrote:

Aman, thank you for pointing this out. This is something I should have remarked on in the main article - I’ll update it in a second. The problem is that our internal codebase has moved on a little since RabbitMQ 1.3.0 was released, so there are actually two branches in the rabbitmq-stomp mercurial repository: default, which tracks the main branch of the server, and rabbitmqv130branch, which compiles against the released 1.3.0 release of the server.

The diff you’ve provided catches one of the three areas where the two stomp adapter differ; there are a couple of similar changes that need to be made.

Does it work out-of-the-box if, once you have performed your “hg clone” step, you run “hg update rabbitmqv130branch”, to switch to the maintenance branch, before compiling and running?

On 26 August, 2008 at 1:57 am, jake mallory wrote:

What are the known limitations for the stomp gateway?

On 28 August, 2008 at 12:22 pm, tonyg wrote:

@jake: It implements all the protocol described at http://stomp.codehaus.org/Protocol, hopefully faithfully. There are quite a few AMQP features that are not yet exposed via the STOMP gateway, though; for those, you’ll need to either use a real AMQP client, or send us a patch! (Patches welcome :-) )

On 29 August, 2008 at 5:49 pm, jake mallory wrote:

thanks. I found ruby-ampq (http://github.com/tmm1/amqp/wikis) and qpid (http://cwiki.apache.org/qpid) which I might try instead.

On 23 November, 2008 at 10:11 pm, Hans wrote:

Hi Tony,

I can not find your uploaded file, nor can your mercurial system. Could you upload it again, please? Thanks

On 24 November, 2008 at 12:07 pm, tonyg wrote:

Hi Hans,

Sorry about that — we’ve moved the STOMP adapter to our RabbitMQ hg repository: http://hg.rabbitmq.com/rabbitmq-stomp/

A snapshot can be downloaded from http://hg.rabbitmq.com/rabbitmq-stomp/archive/tip.zip.

On 8 January, 2009 at 10:22 pm, sidda wrote:

Hi Tony,

I ran the rabbitmq-stomp adapter to verify the Stomp client that we have implemented at Kaazing. It works well - thank you.

In the process of running rabbitmq-stomp adapter with RabbitMQ, I noticed that content-length and content-type headers are added stomp frames where required. But, for the ERROR frame the error message is in text and the content-length header set, but the content-type header is not set.

My code, being a generic STOMP client, cannot assume that the content of the ERROR frame is always going to be text - as content-length header is typically used by STOMP servers to send binary data. So, I am writing this comment to find out if you can add content-type header to the ERROR frame in rabbitmq-stomp adapter. I can provide you with the patch required (it is a one line change).

Best Regards,
Sidda

On 29 January, 2009 at 3:10 pm, tonyg wrote:

Sidda, that’s a good suggestion. Thanks! I’ve just committed a patch that sets a content-type of text/plain on error messages. The changeset is here: http://hg.rabbitmq.com/rabbitmq-stomp/rev/1a1ff5067bb6

On 24 March, 2009 at 5:17 pm, Sig wrote:

I’m receiving the following with a custom erl built in /usr/local

make -C rabbitmq-stomp-rabbitmq_v1_3_0_branch run
make: Entering directory '/usr/local/src/rabbitmq-stomp-rabbitmq_v1_3_0_branch'
make -C ../rabbitmq-1.3.0/erlang/rabbit run \
        RABBIT_ARGS='-pa '"$(pwd)/ebin"' -rabbit \
            stomp_listeners [{\"0.0.0.0\",61613}] \
            extra_startup_steps [{\"STOMP-listeners\",rabbit_stomp,kickstart,[]}]’
make[1]: Entering directory ‘/usr/local/src/rabbitmq-1.3.0/erlang/rabbit’
NODE_IP_ADDRESS= NODE_PORT= NODE_ONLY=true LOG_BASE=/tmp  RABBIT_ARGS=”-pa /usr/local/src/rabbitmq-stomp-rabbitmq_v1_3_0_branch/ebin -rabbit
        stomp_listeners [{\"0.0.0.0\",61613}]
        extra_startup_steps [{\"STOMP-listeners\",rabbit_stomp,kickstart,[]}] -s rabbit” MNESIA_DIR=/tmp/rabbitmq-rabbit-mnesia ./scripts/rabbitmq-server
{error_logger,{{2009,3,24},{10,20,27}},”Protocol: ~p: register error: ~p~n”,["inet_tcp",{{badmatch,{error,duplicate_name}},[{inet_tcp_dist,listen,1},{net_kernel,start_protos,4},{net_kernel,start_protos,3},{net_kernel,init_node,2},{net_kernel,init,1},{gen_server,init_it,6},{proc_lib,init_p,5}]}]}
{error_logger,{{2009,3,24},{10,20,27}},crash_report,[[{pid,<0.21.0>},{registered_name,net_kernel},{error_info,{error,badarg}},{initial_call,{gen,init_it,[gen_server,<0.18.0>,<0.18.0>,{local,net_kernel},net_kernel,{rabbit,shortnames,15000},[]]}},{ancestors,[net_sup,kernel_sup,<0.9.0>]},{messages,[]},{links,[#Port<0.8>,<0.18.0>]},{dictionary,[{longnames,false}]},{trap_exit,true},{status,running},{heap_size,233},{stack_size,21},{reductions,481}],[]]}
{error_logger,{{2009,3,24},{10,20,27}},supervisor_report,[{supervisor,{local,net_sup}},{errorContext,start_error},{reason,{'EXIT',nodistribution}},{offender,[{pid,undefined},{name,net_kernel},{mfa,{net_kernel,start_link,[[rabbit,shortnames]]}},{restart_type,permanent},{shutdown,2000},{child_type,worker}]}]}
{error_logger,{{2009,3,24},{10,20,27}},supervisor_report,[{supervisor,{local,kernel_sup}},{errorContext,start_error},{reason,shutdown},{offender,[{pid,undefined},{name,net_sup},{mfa,{erl_distribution,start_link,[]}},{restart_type,permanent},{shutdown,infinity},{child_type,supervisor}]}]}
{error_logger,{{2009,3,24},{10,20,27}},crash_report,[[{pid,<0.8.0>},{registered_name,[]},{error_info,{shutdown,{kernel,start,[normal,[]]}}},{initial_call,{application_master,init,[<0.6.0>,<0.7.0>,{appl_data,kernel,[application_controller,erl_reply,auth,boot_server,code_server,disk_log_server,disk_log_sup,erl_prim_loader,error_logger,file_server_2,fixtable_server,global_group,global_name_server,heart,init,kernel_config,kernel_sup,net_kernel,net_sup,rex,user,os_server,ddll_server,erl_epmd,inet_db,pg2],undefined,{kernel,[]},[application,application_controller,application_master,application_starter,auth,code,code_aux,packages,code_server,dist_util,erl_boot_server,erl_distribution,erl_prim_loader,erl_reply,erlang,error_handler,error_logger,file,file_server,file_io_server,prim_file,global,global_group,global_search,group,heart,hipe_unified_loader,inet6_tcp,inet6_tcp_dist,inet6_udp,inet_config,inet_hosts,inet_gethost_native,inet_tcp_dist,init,kernel,kernel_config,net,net_adm,net_kernel,os,ram_file,rpc,user,user_drv,user_sup,disk_log,disk_log_1,disk_log_server,disk_log_sup,dist_ac,erl_ddll,erl_epmd,erts_debug,gen_tcp,gen_udp,gen_sctp,prim_inet,inet,inet_db,inet_dns,inet_parse,inet_res,inet_tcp,inet_udp,inet_sctp,pg2,seq_trace,wrap_log_reader,zlib,otp_ring0],[],infinity,infinity},normal]}},{ancestors,[<0.7.0>]},{messages,[{'EXIT',<0.9.0>,normal}]},{links,[<0.7.0>,<0.6.0>]},{dictionary,[]},{trap_exit,true},{status,running},{heap_size,987},{stack_size,21},{reductions,2063}],[]]}
{error_logger,{{2009,3,24},{10,20,27}},std_info,[{application,kernel},{exited,{shutdown,{kernel,start,[normal,[]]}}},{type,permanent}]}
{”Kernel pid terminated”,application_controller,”{application_start_failure,kernel,{shutdown,{kernel,start,[normal,[]]}}}”}

On 25 March, 2009 at 10:00 am, tonyg wrote:

@Sig: could the problem be that you already have another RabbitMQ instance started? Using “make run” causes a whole broker instance with embedded STOMP adapter to start.

Also, these days, you should probably use the default branch if you can, not the 1.3 support branch; the latest release of RabbitMQ is 1.5.3, and there’s a corresponding STOMP adapter tag. Try this, after ensuring that RabbitMQ is not running:

hg clone http://hg.rabbitmq.com/rabbitmq-server
hg clone http://hg.rabbitmq.com/rabbitmq-codegen
hg clone http://hg.rabbitmq.com/rabbitmq-stomp
(cd rabbitmq-server; hg up rabbitmq_v1_5_3)
(cd rabbitmq-codegen; hg up rabbitmq_v1_5_3)
(cd rabbitmq-stomp; hg up rabbitmq_v1_5_3)
make -C rabbitmq-server
make -C rabbitmq-stomp run

On 25 March, 2009 at 10:12 am, tonyg wrote:

For those who haven’t yet seen it: the RabbitMQ mailing list is a great place to get help using RabbitMQ, including configuring the STOMP adapter etc. You can subscribe etc via http://lists.rabbitmq.com/cgi-bin/mailman/listinfo/rabbitmq-discuss.

On 26 March, 2009 at 2:50 am, Sig wrote:

Thank You!

I finally realized that I had a version running in the background. . I still might join the mailing list, this is a very impressive project.

Cheers