Commit 2ac684f5 authored by Federico Marani's avatar Federico Marani

* implemented STOMP support for external message queueing systems

* (ticket-724)
* included PHP STOMP libraries (http://code.google.com/p/stompcli/)
parent 09bab904
......@@ -112,6 +112,14 @@ $config['sphinx']['port'] = 3312;
#background. See the README for details.
#$config['queue']['enabled'] = true;
#Queue subsystem
#subsystems: internal (default) or stomp
#using stomp requires an external message queue server
#$config['queue']['subsystem'] = 'stomp';
#$config['queue']['stomp_server'] = 'tcp://localhost:61613';
#use different queue_basename for each laconica instance managed by the server
#$config['queue']['queue_basename'] = 'laconica';
#The following customise the behaviour of the various daemons:
#$config['daemon']['piddir'] = '/var/run';
#$config['daemon']['user'] = false;
......
This diff is collapsed.
<?php
/**
*
* Copyright 2005-2006 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* vim: set expandtab tabstop=3 shiftwidth=3: */
/**
* A Stomp Connection
*
*
* @package Stomp
* @author Michael Caplan <mcaplan@labnet.net>
* @version $Revision: 23 $
*/
class Stomp_Exception extends Exception
{
protected $_details;
/**
* Constructor
*
* @param string $message Error message
* @param int $code Error code
* @param string $details Stomp server error details
*/
public function __construct($message = null, $code = 0, $details = '')
{
$this->_details = $details;
parent::__construct($message, $code);
}
/**
* Stomp server error details
*
* @return string
*/
public function getDetails()
{
return $this->_details;
}
}
?>
\ No newline at end of file
<?php
/**
*
* Copyright 2005-2006 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* vim: set expandtab tabstop=3 shiftwidth=3: */
/**
* Stomp Frames are messages that are sent and received on a StompConnection.
*
* @package Stomp
* @author Hiram Chirino <hiram@hiramchirino.com>
* @author Dejan Bosanac <dejan@nighttale.net>
* @author Michael Caplan <mcaplan@labnet.net>
* @version $Revision: 36 $
*/
class Stomp_Frame
{
public $command;
public $headers = array();
public $body;
/**
* Constructor
*
* @param string $command
* @param array $headers
* @param string $body
*/
public function __construct ($command = null, $headers = null, $body = null)
{
$this->_init($command, $headers, $body);
}
protected function _init ($command = null, $headers = null, $body = null)
{
$this->command = $command;
if ($headers != null) {
$this->headers = $headers;
}
$this->body = $body;
if ($this->command == 'ERROR') {
require_once 'Stomp/Exception.php';
throw new Stomp_Exception($this->headers['message'], 0, $this->body);
}
}
/**
* Convert frame to transportable string
*
* @return string
*/
public function __toString()
{
$data = $this->command . "\n";
foreach ($this->headers as $name => $value) {
$data .= $name . ": " . $value . "\n";
}
$data .= "\n";
$data .= $this->body;
return $data .= "\x00\n";
}
}
?>
\ No newline at end of file
<?php
/**
*
* Copyright 2005-2006 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* vim: set expandtab tabstop=3 shiftwidth=3: */
require_once 'Stomp/Frame.php';
/**
* Basic text stomp message
*
* @package Stomp
* @author Dejan Bosanac <dejan@nighttale.net>
* @version $Revision: 23 $
*/
class Stomp_Message extends Stomp_Frame
{
public function __construct ($body, $headers = null)
{
$this->_init("SEND", $headers, $body);
}
}
?>
\ No newline at end of file
<?php
/**
*
* Copyright 2005-2006 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* vim: set expandtab tabstop=3 shiftwidth=3: */
require_once 'Stomp/Message.php';
/**
* Message that contains a stream of uninterpreted bytes
*
* @package Stomp
* @author Dejan Bosanac <dejan@nighttale.net>
* @version $Revision: 23 $
*/
class Stomp_Message_Bytes extends Stomp_Message
{
/**
* Constructor
*
* @param string $body
* @param array $headers
*/
function __construct ($body, $headers = null)
{
$this->_init("SEND", $headers, $body);
if ($this->headers == null) {
$this->headers = array();
}
$this->headers['content-length'] = count($body);
}
}
?>
\ No newline at end of file
<?php
/**
*
* Copyright 2005-2006 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* vim: set expandtab tabstop=3 shiftwidth=3: */
require_once 'Stomp/Message.php';
/**
* Message that contains a set of name-value pairs
*
* @package Stomp
* @author Dejan Bosanac <dejan@nighttale.net>
* @version $Revision: 23 $
*/
class Stomp_Message_Map extends Stomp_Message
{
public $map;
/**
* Constructor
*
* @param Stomp_Frame|string $msg
* @param array $headers
*/
function __construct ($msg, $headers = null)
{
if ($msg instanceof Stomp_Frame) {
$this->_init($msg->command, $msg->headers, $msg->body);
$this->map = json_decode($msg->body);
} else {
$this->_init("SEND", $headers, $msg);
if ($this->headers == null) {
$this->headers = array();
}
$this->headers['amq-msg-type'] = 'MapMessage';
$this->body = json_encode($msg);
}
}
}
?>
\ No newline at end of file
......@@ -75,15 +75,9 @@ class QueueHandler extends Daemon
return true;
}
function run()
{
if (!$this->start()) {
return false;
}
$this->log(LOG_INFO, 'checking for queued notices');
$transport = $this->transport();
function db_dispatch() {
do {
$qi = Queue_item::top($transport);
$qi = Queue_item::top($this->transport());
if ($qi) {
$this->log(LOG_INFO, 'Got item enqueued '.common_exact_date($qi->created));
$notice = Notice::staticGet($qi->notice_id);
......@@ -115,6 +109,68 @@ class QueueHandler extends Daemon
$this->idle(5);
}
} while (true);
}
function stomp_dispatch() {
require("Stomp.php");
$con = new Stomp(common_config('queue','stomp_server'));
if (!$con->connect()) {
$this->log(LOG_ERR, 'Failed to connect to queue server');
return false;
}
$queue_basename = common_config('queue','queue_basename');
// subscribe to the relevant queue (format: basename-transport)
$con->subscribe('/queue/'.$queue_basename.'-'.$this->transport());
do {
$frame = $con->readFrame();
if ($frame) {
$this->log(LOG_INFO, 'Got item enqueued '.common_exact_date($frame->headers['created']));
// XXX: Now the queue handler receives only the ID of the
// notice, and it has to get it from the DB
// A massive improvement would be avoid DB query by transmitting
// all the notice details via queue server...
$notice = Notice::staticGet($frame->body);
if ($notice) {
$this->log(LOG_INFO, 'broadcasting notice ID = ' . $notice->id);
$result = $this->handle_notice($notice);
if ($result) {
// if the msg has been handled positively, ack it
// and the queue server will remove it from the queue
$con->ack($frame);
$this->log(LOG_INFO, 'finished broadcasting notice ID = ' . $notice->id);
}
else {
// no ack
$this->log(LOG_WARNING, 'Failed broadcast for notice ID = ' . $notice->id);
}
$notice->free();
unset($notice);
$notice = null;
} else {
$this->log(LOG_WARNING, 'queue item for notice that does not exist');
}
}
} while (true);
$con->disconnect();
}
function run()
{
if (!$this->start()) {
return false;
}
$this->log(LOG_INFO, 'checking for queued notices');
if (common_config('queue','subsystem') == 'stomp') {
$this->stomp_dispatch();
}
else {
$this->db_dispatch();
}
if (!$this->finish()) {
return false;
}
......@@ -143,4 +199,4 @@ class QueueHandler extends Daemon
common_log($level, $this->class_name() . ' ('. $this->get_id() .'): '.$msg);
}
}
\ No newline at end of file
......@@ -1138,18 +1138,44 @@ function common_twitter_broadcast($notice, $flink)
function common_enqueue_notice($notice)
{
foreach (array('jabber', 'omb', 'sms', 'public') as $transport) {
$qi = new Queue_item();
$qi->notice_id = $notice->id;
$qi->transport = $transport;
$qi->created = $notice->created;
$result = $qi->insert();
if (!$result) {
$last_error = &PEAR::getStaticProperty('DB_DataObject','lastError');
common_log(LOG_ERR, 'DB error inserting queue item: ' . $last_error->message);
return false;
if (common_config('queue','subsystem') == 'stomp') {
// use an external message queue system via STOMP
require_once("Stomp.php");
$con = new Stomp(common_config('queue','stomp_server'));
if (!$con->connect()) {
common_log(LOG_ERR, 'Failed to connect to queue server');
return false;
}
$queue_basename = common_config('queue','queue_basename');
foreach (array('jabber', 'omb', 'sms', 'public') as $transport) {
if (!$con->send(
'/queue/'.$queue_basename.'-'.$transport, // QUEUE
$notice->id, // BODY of the message
array ( // HEADERS of the msg
'created' => $notice->created
))) {
common_log(LOG_ERR, 'Error sending to '.$transport.' queue');
return false;
}
common_log(LOG_DEBUG, 'complete remote queueing notice ID = ' . $notice->id . ' for ' . $transport);
}
$result = true;
}
else {
// in any other case, 'internal'
foreach (array('jabber', 'omb', 'sms', 'public') as $transport) {
$qi = new Queue_item();
$qi->notice_id = $notice->id;
$qi->transport = $transport;
$qi->created = $notice->created;
$result = $qi->insert();
if (!$result) {
$last_error = &PEAR::getStaticProperty('DB_DataObject','lastError');
common_log(LOG_ERR, 'DB error inserting queue item: ' . $last_error->message);
return false;
}
common_log(LOG_DEBUG, 'complete queueing notice ID = ' . $notice->id . ' for ' . $transport);
}
common_log(LOG_DEBUG, 'complete queueing notice ID = ' . $notice->id . ' for ' . $transport);
}
return $result;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment