Several fixes to make RabbitMQ a player.

* extlib/Stomp.php
    -spaces for tabs (we're on PEAR, right?)
    - send: initialize the $properties parameter as array() instead of null
      this prevents unsetting $headers if $properties was not set
      (besides that, it's the proper way to initialize an array)
    - subscribe: insert FIXME's on ActiveMQ specifics
    - ack: make sure the content-length header is set *and* is zero.
      I have seen the header set to '3' there but could not find where it
      came from, this is at least safe.
    - disconnect: typo in $headers variable
    - readFrame: use fgets() instead of gets() so that RabbitQ, which is more protocol strict can also play
  * extlib/Stomp/Frame.php
    - spaces for tabs
    - add note on possibly protocol violating linefeed
  * extlib/Stomp/Message.php
    - space for tabs
    - add content-length header for message
  * lib/stompqueuemanager.php
    - use the notice for logging, not the frame
This commit is contained in:
Marcel van der Boom 2009-09-08 22:21:33 +02:00 committed by Craig Andrews
parent a73162d3eb
commit c04987018c
4 changed files with 123 additions and 99 deletions

View File

@ -26,7 +26,7 @@ require_once 'Stomp/Frame.php';
* *
* @package Stomp * @package Stomp
* @author Hiram Chirino <hiram@hiramchirino.com> * @author Hiram Chirino <hiram@hiramchirino.com>
* @author Dejan Bosanac <dejan@nighttale.net> * @author Dejan Bosanac <dejan@nighttale.net>
* @author Michael Caplan <mcaplan@labnet.net> * @author Michael Caplan <mcaplan@labnet.net>
* @version $Revision: 43 $ * @version $Revision: 43 $
*/ */
@ -44,15 +44,15 @@ class Stomp
* *
* @var int * @var int
*/ */
public $prefetchSize = 1; public $prefetchSize = 1;
/** /**
* Client id used for durable subscriptions * Client id used for durable subscriptions
* *
* @var string * @var string
*/ */
public $clientId = null; public $clientId = null;
protected $_brokerUri = null; protected $_brokerUri = null;
protected $_socket = null; protected $_socket = null;
protected $_hosts = array(); protected $_hosts = array();
@ -66,7 +66,7 @@ class Stomp
protected $_sessionId; protected $_sessionId;
protected $_read_timeout_seconds = 60; protected $_read_timeout_seconds = 60;
protected $_read_timeout_milliseconds = 0; protected $_read_timeout_milliseconds = 0;
/** /**
* Constructor * Constructor
* *
@ -134,10 +134,10 @@ class Stomp
require_once 'Stomp/Exception.php'; require_once 'Stomp/Exception.php';
throw new Stomp_Exception("No broker defined"); throw new Stomp_Exception("No broker defined");
} }
// force disconnect, if previous established connection exists // force disconnect, if previous established connection exists
$this->disconnect(); $this->disconnect();
$i = $this->_currentHost; $i = $this->_currentHost;
$att = 0; $att = 0;
$connected = false; $connected = false;
@ -190,11 +190,11 @@ class Stomp
if ($password != '') { if ($password != '') {
$this->_password = $password; $this->_password = $password;
} }
$headers = array('login' => $this->_username , 'passcode' => $this->_password); $headers = array('login' => $this->_username , 'passcode' => $this->_password);
if ($this->clientId != null) { if ($this->clientId != null) {
$headers["client-id"] = $this->clientId; $headers["client-id"] = $this->clientId;
} }
$frame = new Stomp_Frame("CONNECT", $headers); $frame = new Stomp_Frame("CONNECT", $headers);
$this->_writeFrame($frame); $this->_writeFrame($frame);
$frame = $this->readFrame(); $frame = $this->readFrame();
if ($frame instanceof Stomp_Frame && $frame->command == 'CONNECTED') { if ($frame instanceof Stomp_Frame && $frame->command == 'CONNECTED') {
@ -209,7 +209,7 @@ class Stomp
} }
} }
} }
/** /**
* Check if client session has ben established * Check if client session has ben established
* *
@ -229,7 +229,7 @@ class Stomp
return $this->_sessionId; return $this->_sessionId;
} }
/** /**
* Send a message to a destination in the messaging system * Send a message to a destination in the messaging system
* *
* @param string $destination Destination queue * @param string $destination Destination queue
* @param string|Stomp_Frame $msg Message * @param string|Stomp_Frame $msg Message
@ -237,7 +237,7 @@ class Stomp
* @param boolean $sync Perform request synchronously * @param boolean $sync Perform request synchronously
* @return boolean * @return boolean
*/ */
public function send ($destination, $msg, $properties = null, $sync = null) public function send ($destination, $msg, $properties = array(), $sync = null)
{ {
if ($msg instanceof Stomp_Frame) { if ($msg instanceof Stomp_Frame) {
$msg->headers['destination'] = $destination; $msg->headers['destination'] = $destination;
@ -319,10 +319,12 @@ class Stomp
public function subscribe ($destination, $properties = null, $sync = null) public function subscribe ($destination, $properties = null, $sync = null)
{ {
$headers = array('ack' => 'client'); $headers = array('ack' => 'client');
$headers['activemq.prefetchSize'] = $this->prefetchSize; // FIXME: this seems to be activemq specific, but not hurting rabbitmq?
if ($this->clientId != null) { $headers['activemq.prefetchSize'] = $this->prefetchSize;
$headers["activemq.subcriptionName"] = $this->clientId; if ($this->clientId != null) {
} // FIXME: this seems to be activemq specific, but not hurting rabbitmq?
$headers["activemq.subcriptionName"] = $this->clientId;
}
if (isset($properties)) { if (isset($properties)) {
foreach ($properties as $name => $value) { foreach ($properties as $name => $value) {
$headers[$name] = $value; $headers[$name] = $value;
@ -424,7 +426,7 @@ class Stomp
} }
/** /**
* Acknowledge consumption of a message from a subscription * Acknowledge consumption of a message from a subscription
* Note: This operation is always asynchronous * Note: This operation is always asynchronous
* *
* @param string|Stomp_Frame $messageMessage ID * @param string|Stomp_Frame $messageMessage ID
* @param string $transactionId * @param string $transactionId
@ -433,20 +435,26 @@ class Stomp
*/ */
public function ack ($message, $transactionId = null) public function ack ($message, $transactionId = null)
{ {
// Handle the headers,
$headers = array();
if ($message instanceof Stomp_Frame) { if ($message instanceof Stomp_Frame) {
$frame = new Stomp_Frame('ACK', $message->headers); // Copy headers from the object
$this->_writeFrame($frame); // FIXME: at least content-length can be wrong here (set to 3 sometimes).
return true; $headers = $message->headers;
} else { } else {
$headers = array();
if (isset($transactionId)) { if (isset($transactionId)) {
$headers['transaction'] = $transactionId; $headers['transaction'] = $transactionId;
} }
$headers['message-id'] = $message; $headers['message-id'] = $message;
$frame = new Stomp_Frame('ACK', $headers);
$this->_writeFrame($frame);
return true;
} }
// An ACK has no content
$headers['content-length'] = 0;
// Create it and write it out
$frame = new Stomp_Frame('ACK', $headers);
$this->_writeFrame($frame);
return true;
} }
/** /**
* Graceful disconnect from the server * Graceful disconnect from the server
@ -454,11 +462,11 @@ class Stomp
*/ */
public function disconnect () public function disconnect ()
{ {
$headers = array(); $headers = array();
if ($this->clientId != null) { if ($this->clientId != null) {
$headers["client-id"] = $this->clientId; $headers["client-id"] = $this->clientId;
} }
if (is_resource($this->_socket)) { if (is_resource($this->_socket)) {
$this->_writeFrame(new Stomp_Frame('DISCONNECT', $headers)); $this->_writeFrame(new Stomp_Frame('DISCONNECT', $headers));
@ -490,19 +498,19 @@ class Stomp
$this->_writeFrame($stompFrame); $this->_writeFrame($stompFrame);
} }
} }
/** /**
* Set timeout to wait for content to read * Set timeout to wait for content to read
* *
* @param int $seconds_to_wait Seconds to wait for a frame * @param int $seconds_to_wait Seconds to wait for a frame
* @param int $milliseconds Milliseconds to wait for a frame * @param int $milliseconds Milliseconds to wait for a frame
*/ */
public function setReadTimeout($seconds, $milliseconds = 0) public function setReadTimeout($seconds, $milliseconds = 0)
{ {
$this->_read_timeout_seconds = $seconds; $this->_read_timeout_seconds = $seconds;
$this->_read_timeout_milliseconds = $milliseconds; $this->_read_timeout_milliseconds = $milliseconds;
} }
/** /**
* Read responce frame from server * Read responce frame from server
* *
@ -513,19 +521,29 @@ class Stomp
if (!$this->hasFrameToRead()) { if (!$this->hasFrameToRead()) {
return false; return false;
} }
$rb = 1024; $rb = 1024;
$data = ''; $data = '';
do { do {
$read = fgets($this->_socket, $rb); $read = fread($this->_socket, $rb);
if ($read === false) { if ($read === false) {
$this->_reconnect(); $this->_reconnect();
return $this->readFrame(); return $this->readFrame();
} }
$data .= $read; $data .= $read;
$len = strlen($data); $len = strlen($data);
} while (($len < 2 || ! ($data[$len - 2] == "\x00" && $data[$len - 1] == "\n")));
$continue = true;
// ActiveMq apparently add \n after 0 char
if($data[$len - 2] == "\x00" && $data[$len - 1] == "\n") {
$continue = false;
}
// RabbitMq does not
if($data[$len - 1] == "\x00") {
$continue = false;
}
} while ( $continue );
list ($header, $body) = explode("\n\n", $data, 2); list ($header, $body) = explode("\n\n", $data, 2);
$header = explode("\n", $header); $header = explode("\n", $header);
$headers = array(); $headers = array();
@ -546,7 +564,7 @@ class Stomp
return $frame; return $frame;
} }
} }
/** /**
* Check if there is a frame to read * Check if there is a frame to read
* *
@ -557,7 +575,7 @@ class Stomp
$read = array($this->_socket); $read = array($this->_socket);
$write = null; $write = null;
$except = null; $except = null;
$has_frame_to_read = stream_select($read, $write, $except, $this->_read_timeout_seconds, $this->_read_timeout_milliseconds); $has_frame_to_read = stream_select($read, $write, $except, $this->_read_timeout_seconds, $this->_read_timeout_milliseconds);
if ($has_frame_to_read === false) { if ($has_frame_to_read === false) {
@ -565,18 +583,18 @@ class Stomp
} else if ($has_frame_to_read > 0) { } else if ($has_frame_to_read > 0) {
return true; return true;
} else { } else {
return false; return false;
} }
} }
/** /**
* Reconnects and renews subscriptions (if there were any) * Reconnects and renews subscriptions (if there were any)
* Call this method when you detect connection problems * Call this method when you detect connection problems
*/ */
protected function _reconnect () protected function _reconnect ()
{ {
$subscriptions = $this->_subscriptions; $subscriptions = $this->_subscriptions;
$this->connect($this->_username, $this->_password); $this->connect($this->_username, $this->_password);
foreach ($subscriptions as $dest => $properties) { foreach ($subscriptions as $dest => $properties) {
$this->subscribe($dest, $properties); $this->subscribe($dest, $properties);

View File

@ -17,46 +17,46 @@
*/ */
/* vim: set expandtab tabstop=3 shiftwidth=3: */ /* vim: set expandtab tabstop=3 shiftwidth=3: */
/** /**
* Stomp Frames are messages that are sent and received on a StompConnection. * Stomp Frames are messages that are sent and received on a StompConnection.
* *
* @package Stomp * @package Stomp
* @author Hiram Chirino <hiram@hiramchirino.com> * @author Hiram Chirino <hiram@hiramchirino.com>
* @author Dejan Bosanac <dejan@nighttale.net> * @author Dejan Bosanac <dejan@nighttale.net>
* @author Michael Caplan <mcaplan@labnet.net> * @author Michael Caplan <mcaplan@labnet.net>
* @version $Revision: 36 $ * @version $Revision: 36 $
*/ */
class Stomp_Frame class Stomp_Frame
{ {
public $command; public $command;
public $headers = array(); public $headers = array();
public $body; public $body;
/** /**
* Constructor * Constructor
* *
* @param string $command * @param string $command
* @param array $headers * @param array $headers
* @param string $body * @param string $body
*/ */
public function __construct ($command = null, $headers = null, $body = null) public function __construct ($command = null, $headers = null, $body = null)
{ {
$this->_init($command, $headers, $body); $this->_init($command, $headers, $body);
} }
protected function _init ($command = null, $headers = null, $body = null) protected function _init ($command = null, $headers = null, $body = null)
{ {
$this->command = $command; $this->command = $command;
if ($headers != null) { if ($headers != null) {
$this->headers = $headers; $this->headers = $headers;
} }
$this->body = $body; $this->body = $body;
if ($this->command == 'ERROR') { if ($this->command == 'ERROR') {
require_once 'Stomp/Exception.php'; require_once 'Stomp/Exception.php';
throw new Stomp_Exception($this->headers['message'], 0, $this->body); throw new Stomp_Exception($this->headers['message'], 0, $this->body);
} }
} }
/** /**
@ -74,7 +74,8 @@ class Stomp_Frame
$data .= "\n"; $data .= "\n";
$data .= $this->body; $data .= $this->body;
return $data .= "\x00\n"; $data .= "\x00\n"; // Should there really be a linefeed here?
} return $data;
} }
}
?> ?>

View File

@ -29,8 +29,12 @@ require_once 'Stomp/Frame.php';
*/ */
class Stomp_Message extends Stomp_Frame class Stomp_Message extends Stomp_Frame
{ {
public function __construct ($body, $headers = null) public function __construct ($body, $headers = array())
{ {
if(!isset($headers['content-length'])) {
// TODO: log this, to see if this is correct
$headers['content-length'] = strlen($body);
}
$this->_init("SEND", $headers, $body); $this->_init("SEND", $headers, $body);
} }
} }

View File

@ -141,10 +141,11 @@ class StompQueueManager
$this->con->ack($frame); $this->con->ack($frame);
} else { } else {
if ($handler->handle_notice($notice)) { if ($handler->handle_notice($notice)) {
$this->_log(LOG_INFO, 'Successfully handled notice '. $notice->id .' posted at ' . $frame->headers['created'] . ' in queue '. $queue); $this->_log(LOG_INFO, 'Successfully handled notice '. $notice->id .' originally posted at ' . $notice->created . ' in queue '. $queue);
$this->con->ack($frame); $this->con->ack($frame);
} else { } else {
$this->_log(LOG_WARNING, 'Failed handling notice '. $notice->id .' posted at ' . $frame->headers['created'] . ' in queue '. $queue); $this->_log(LOG_WARNING, 'Failed handling notice '. $notice->id .' originally posted at ' . $notice->created . ' in queue '. $queue);
// FIXME we probably shouldn't have to do // FIXME we probably shouldn't have to do
// this kind of queue management ourselves // this kind of queue management ourselves
$this->con->ack($frame); $this->con->ack($frame);