stomp.py

“stomp.py” is a Python client library for accessing messaging servers (such as ActiveMQ or JBoss Messaging) using the STOMP protocol. It can also be run as a standalone, command-line client for testing.

It was created, because I experienced strange bugs with STOMPy, and wanted an apache-licensed library.

You can download the latest version from here, or checkout from the Mercurial repository here (see this blog entry for a bit of background on the repos… only if interested).

CONTRIBUTORS
Julian Scheid (Rising Sun Pictures - http://open.rsp.com.au)
Andreas Schobel


A simple example of using stomp.py is:

import time
import sys

import stomp

class MyListener(object):
    def on_error(self, headers, message):
        print 'received an error %s' % message

    def on_message(self, headers, message):
        print 'received a message %s' % message

conn = stomp.Connection()
conn.add_listener(MyListener())
conn.start()
conn.connect()

conn.subscribe(destination='/queue/test', ack='auto')

conn.send(' '.join(sys.argv[1:]), destination='/queue/test')

time.sleep(2)
conn.disconnect()

You can also use it from the command line by running:

python stomp.py localhost 61613

or

jython stomp.py localhost 61613

Then typing commands such as:

subscribe /queue/test
send /queue/test hello world

RSS feed | Trackback URI

25 Comments »

Comment by Ime Akpan
2007-08-15 09:50:52

Hello,

I have recently started learning python and am currently on a project that requires me to use stomp. I noticed that your stomp.py example was posted a while ago.
My question is, how does stomp.py compare to pyactivemq? I am downloading stomp.py but would like to get your honest opinion.
Also, do you know of any good stomp tutorials out there for python

Thank you,
Ime

 
Comment by jrbriggs
2007-08-15 10:45:56

To be honest, I have no experience at all with pyactivemq. This is the first I’ve seen of it.

stomp.py was a quick hack so I could experiment with activemq, but I don’t currently have any need to use activemq, thus I haven’t done any development on this code for a while. If pyactivemq is under active development, it’s probably the preferable option.

About the only advantages I can say my version has are:

1. if you remove the “import select” at the top of the file, it will run as is with Jython, I believe. (I must get around to updating it myself).

2. the code is probably easier to understand if you’re learning

Sorry but I don’t know of any stomp tutorials out there.

 
Comment by Jason Cater
2007-11-20 16:56:45

There’s a typo in your example code. The line:

conn.addlistener(MyListener())

should read:

conn.add_listener(MyListener())

 
Comment by Jason Cater
2007-11-20 17:17:19

Hmm, you’ve also changed the syntax of commands with the latest commit.

This no longer works:

conn.subscribe(’/queue/test’)

Instead, use:

conn.subscribe(destination=’/queue/test’)

Comment by jrbriggs
2007-11-20 18:14:43

Right on both counts. Julian made same changes to my original code, to tidy things up, and improve functionality/performance/etc. He warned me I needed to update the example code… and I promptly forgot.

Thanks for the update.

 
 
Comment by Simon
2007-12-24 05:06:55

Any idea when you’ll have a chance to update the example code?

Comment by jrbriggs
2007-12-27 13:27:38

Now… ;-)

Fixed an initial connection problem in the stomp code (I think), and the example now works (although with intermittent errors when running with Jython).

 
 
Comment by Greg Holt
2008-03-01 04:21:00

Hi, I’ve been using stomp.py a bit lately and noticed that the latest version doesn’t seem to work with login/passcodes. Lines 315 to 318 set values into __connect_headers, but those values are never actually used.

I fixed my own copy by altering my line 406 from:
self.__send_frame_helper(’CONNECT’, ”, self.__merge_headers([headers, keyword_headers]), [ ])
to:
self.__send_frame_helper(’CONNECT’, ”, self.__merge_headers([self.__connect_headers, headers, keyword_headers]), [ ])

Comment by jrbriggs
2008-03-08 09:49:18

Hi Greg. Sorry for the delay replying, but I’ve finally applied your suggested change.

 
 
Comment by Greg Holt
2008-03-07 03:31:24

Just another quick patch/suggestion. ActiveMQ cares about the case of header names whereas stomp.py converts all header names to lower case. I fixed that in my copy of stomp.py on line 449 by just removing the .lower(). Likely this area should be revisited. Should underscores really be replaced by dashes? I couldn’t find an official grammar specification for STOMP, so, who knows?

Comment by jrbriggs
2008-03-08 09:59:55

Don’t recally seeing an official spec either. I do recall the dashes being needed for a reason… but I can’t remember the reason. So might not be a valid assumption anyway.

 
 
Comment by Fernando Ciciliati
2008-03-26 00:24:20

Hi Jason!

Your library is GREAT! I’ve been playing with it extensively, both as a standalone client for debugging and as an embedded library in my own code.

It’s pure fun ;)

Thanks a lot!!!

PS.: I have some small improvements to suggest regarding performance. Wouldn’t you like to publish the library under SourceForge or GoogleCode? So we could collaborate…

Comment by jrbriggs
2008-03-26 06:53:15

If they’re minor changes, I’m happy to receive patches. Although, I can be a bit slow to apply said patches at times…

 
 
Comment by Lilians AUVIGNE
2008-04-04 08:14:15

Hi jason,

When I use stomp.py with ActiveMQ and your simple example, I can see a TCP Connection leak with JMX.

After debugging this code, I see that the variable self.__socket is set twice.

I corrected this problem with a lock on this method :

def __attempt_connection(self):
lock.acquire(1)
try:
self.__attempt_connection_no_synchronized()
finally: lock.release()

def __attempt_connection_no_synchronized(self):

there is another problem : If the listener doesn’t have the on_disconnected method , as your sample, an AttributeError is raised.

Corrected with a simple test on this attribute :
# Notify listeners
for listener in self.__listeners:
if hasattr(listener, ‘on_disconnected’):
listener.on_disconnected()

Conctact me, if you want an unified diff.

Best regards and Thanks for your implementation ;)

Comment by jrbriggs
2008-05-20 10:14:31

Sorry for the delay. I’ve finally integrated your suggested change… :-)

 
 
Comment by Tazzo
2008-05-09 01:37:13

Hi Jason,
I had some strange behaviour with the client, some message is correct dequeued but not correct managed by the listener … with your simple example too. After the patch on the method __attempt_connection proposed by Lilians AUVIGNE, everithing work correctly.

 
Comment by Fernando Ciciliati
2008-06-17 04:07:04

Hi Jason! Me again…

This time, a small bugfix for the error:


2008-06-14 19:14:52,028 stomp.py ERROR An unhandled exception was encountered in the stomp receiver loop
Traceback (most recent call last):
File "/opt/lwb/eventmanager/stomp.py", line 507, in __receiver_loop
self.__attempt_connection()
File "/opt/lwb/eventmanager/stomp.py", line 663, in __attempt_connection
((self.__reconnect_sleep_initial / (1.0 + self.__reconnect_sleep_increase))
OverflowError: math range error

I have two daemons using stomp.py running on different machines on a fail-over configuration.
The instance that is waiting for the availability of ActiveMQ finally dies with an overflow error
because the ’sleep_exp’ variable is being incremented even if ’sleep_duration’ is greater than
‘__reconnect_sleep_max’. This is the fix I’ve implemented:


--- stomp.py 2008-06-16 16:57:01.000000000 +0100
+++ stomp.py.new 2008-06-16 17:00:23.000000000 +0100
@@ -672,7 +672,8 @@
while self.__running and time.time() < sleep_end:
time.sleep(0.2)

- sleep_exp += 1
+ if sleep_duration < self.__reconnect_sleep_max:
+ sleep_exp += 1
finally:
lock.release()

Thanks!!!

 
Comment by Julien
2008-07-08 19:53:21

Hi,

I try to use stomp.py to communicate with ActiveMQ, everything is working fine, except one thing :
At the CONNECT request, ActiveMQ throws a (non fatal) exception :
DEBUG Transport - Transport failed: java.io.EOFException
java.io.EOFException
at java.io.DataInputStream.readByte(DataInputStream.java:243)
at org.apache.activemq.transport.stomp.StompWireFormat.readLine(StompWireFormat.java:186)
at org.apache.activemq.transport.stomp.StompWireFormat.unmarshal(StompWireFormat.java:94)
at org.apache.activemq.transport.tcp.TcpTransport.readCommand(TcpTransport.java:203)
at org.apache.activemq.transport.tcp.TcpTransport.doRun(TcpTransport.java:195)
at org.apache.activemq.transport.tcp.TcpTransport.run(TcpTransport.java:183)
at java.lang.Thread.run(Thread.java:595)

But the connexion is OK.

I’ve tested with a Stomp C Client, but this exception doesn’t arise.

Have you got an idea on what’s happening ?

Thanks in advance…

Comment by jrbriggs
2008-07-09 11:00:29

Hi Julien

I’m afraid my stomp client is only intermittently supported at the moment. I don’t have a requirement to use it myself, so it’s only updated when people send me patches.

Apologies,
J

 
 
Comment by cnobile
2008-09-19 06:19:44

Since the on_connecting() method is called before the connect() method the host and port will always be None. Is it possible to set the __current_host_and_port member object before the TCP connection is made so this value has some meaning in on_connecting()?

Comment by jrbriggs
2008-09-19 08:18:08

connect() issues a Stomp CONNECT. Not the actual socket connect. on_connecting should be called after the TCP connection.

 
 
Comment by steven
2008-10-15 09:24:10

I encountered an irregularly occurring problem with CONNECT not being issued (testing with morbidQ, on Windows).
With debug logging on, I got “An unhandled exception was encountered in the stomp receiver loop”
It would seem that the problem was caused by a socket.error being thrown during in Connection.is_connected:

def is_connected(self):
return self.__socket is not None and self.__socket.getsockname()[1] != 0

Apparently (don’t know much about sockets myself), getsockname() can fail, although there is a socket. And the exception catching in the receiver loop causes the CONNECT to be skipped in that case.

I tried changing it to:

def is_connected(self):
try:
return self.__socket is not None and self.__socket.getsockname()[1] != 0
except socket.error:
return False

And that seems to solve it.

Comment by jrbriggs
2008-10-15 09:40:38

Thanks. Applied the change.

 
 
2008-11-06 20:13:58

Its not everytime you download a library from the internet and it just works without you having to tweak with the code at all. This is one of those, I tried the example and the command line. Everything just works.

Thank You

 
Name (required)
E-mail (required - never shown publicly)
URI
Your Comment (smaller size | larger size)
You may use <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> in your comment.

Trackback responses to this post