| 
									
										
										
										
											2009-06-28 14:38:31 -04:00
										 |  |  | <?php | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2009-08-25 18:12:20 -04:00
										 |  |  |  * StatusNet, the distributed open-source microblogging tool | 
					
						
							| 
									
										
										
										
											2009-06-28 14:38:31 -04:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2010-01-12 19:57:15 -08:00
										 |  |  |  * Abstract class for i/o managers | 
					
						
							| 
									
										
										
										
											2009-06-28 14:38:31 -04:00
										 |  |  |  * | 
					
						
							|  |  |  |  * PHP version 5 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * LICENCE: This program is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU Affero General Public License as published by | 
					
						
							|  |  |  |  * the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  |  * (at your option) any later version. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |  * GNU Affero General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU Affero General Public License | 
					
						
							|  |  |  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @category  QueueManager | 
					
						
							| 
									
										
										
										
											2009-08-25 18:12:20 -04:00
										 |  |  |  * @package   StatusNet | 
					
						
							| 
									
										
										
										
											2009-08-25 18:19:04 -04:00
										 |  |  |  * @author    Evan Prodromou <evan@status.net> | 
					
						
							|  |  |  |  * @author    Sarven Capadisli <csarven@status.net> | 
					
						
							| 
									
										
										
										
											2010-01-12 19:57:15 -08:00
										 |  |  |  * @author    Brion Vibber <brion@status.net> | 
					
						
							|  |  |  |  * @copyright 2009-2010 StatusNet, Inc. | 
					
						
							| 
									
										
										
										
											2009-06-28 14:38:31 -04:00
										 |  |  |  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 | 
					
						
							| 
									
										
										
										
											2009-08-25 18:16:46 -04:00
										 |  |  |  * @link      http://status.net/ | 
					
						
							| 
									
										
										
										
											2009-06-28 14:38:31 -04:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-12 19:57:15 -08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Completed child classes must implement the enqueue() method. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * For background processing, classes should implement either socket-based | 
					
						
							|  |  |  |  * input (handleInput(), getSockets()) or idle-loop polling (idle()). | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | abstract class QueueManager extends IoManager | 
					
						
							| 
									
										
										
										
											2009-06-28 14:38:31 -04:00
										 |  |  | { | 
					
						
							|  |  |  |     static $qm = null; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-21 16:42:50 -08:00
										 |  |  |     public $master = null; | 
					
						
							|  |  |  |     public $handlers = array(); | 
					
						
							|  |  |  |     public $groups = array(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-12 19:57:15 -08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Factory function to pull the appropriate QueueManager object | 
					
						
							|  |  |  |      * for this site's configuration. It can then be used to queue | 
					
						
							|  |  |  |      * events for later processing or to spawn a processing loop. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Plugins can add to the built-in types by hooking StartNewQueueManager. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return QueueManager | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public static function get() | 
					
						
							| 
									
										
										
										
											2009-06-28 14:38:31 -04:00
										 |  |  |     { | 
					
						
							|  |  |  |         if (empty(self::$qm)) { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-07-01 11:34:12 -04:00
										 |  |  |             if (Event::handle('StartNewQueueManager', array(&self::$qm))) { | 
					
						
							| 
									
										
										
										
											2009-06-28 14:38:31 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-07-01 11:34:12 -04:00
										 |  |  |                 $enabled = common_config('queue', 'enabled'); | 
					
						
							| 
									
										
										
										
											2009-07-01 12:09:18 -04:00
										 |  |  |                 $type = common_config('queue', 'subsystem'); | 
					
						
							| 
									
										
										
										
											2009-07-01 11:34:12 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 if (!$enabled) { | 
					
						
							|  |  |  |                     // does everything immediately
 | 
					
						
							| 
									
										
										
										
											2009-07-01 12:09:18 -04:00
										 |  |  |                     self::$qm = new UnQueueManager(); | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                     switch ($type) { | 
					
						
							|  |  |  |                      case 'db': | 
					
						
							|  |  |  |                         self::$qm = new DBQueueManager(); | 
					
						
							|  |  |  |                         break; | 
					
						
							|  |  |  |                      case 'stomp': | 
					
						
							|  |  |  |                         self::$qm = new StompQueueManager(); | 
					
						
							|  |  |  |                         break; | 
					
						
							|  |  |  |                      default: | 
					
						
							|  |  |  |                         throw new ServerException("No queue manager class for type '$type'"); | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2009-06-28 14:38:31 -04:00
										 |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2009-07-04 01:42:42 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return self::$qm; | 
					
						
							| 
									
										
										
										
											2009-06-28 14:38:31 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-12 19:57:15 -08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * @fixme wouldn't necessarily work with other class types. | 
					
						
							|  |  |  |      * Better to change the interface...? | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public static function multiSite() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (common_config('queue', 'subsystem') == 'stomp') { | 
					
						
							|  |  |  |             return IoManager::INSTANCE_PER_PROCESS; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             return IoManager::SINGLE_ONLY; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function __construct() | 
					
						
							| 
									
										
										
										
											2009-06-28 14:38:31 -04:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2010-01-12 19:57:15 -08:00
										 |  |  |         $this->initialize(); | 
					
						
							| 
									
										
										
										
											2009-06-28 14:38:31 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-26 11:49:49 -08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Optional; ping any running queue handler daemons with a notification | 
					
						
							|  |  |  |      * such as announcing a new site to handle or requesting clean shutdown. | 
					
						
							|  |  |  |      * This avoids having to restart all the daemons manually to update configs | 
					
						
							|  |  |  |      * and such. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Called from scripts/queuectl.php controller utility. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param string $event event key | 
					
						
							|  |  |  |      * @param string $param optional parameter to append to key | 
					
						
							|  |  |  |      * @return boolean success | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function sendControlSignal($event, $param='') | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         throw new Exception(get_class($this) . " does not support control signals."); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-12 19:57:15 -08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Store an object (usually/always a Notice) into the given queue | 
					
						
							|  |  |  |      * for later processing. No guarantee is made on when it will be | 
					
						
							|  |  |  |      * processed; it could be immediately or at some unspecified point | 
					
						
							|  |  |  |      * in the future. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Must be implemented by any queue manager. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param Notice $object | 
					
						
							|  |  |  |      * @param string $queue | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     abstract function enqueue($object, $queue); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-21 16:42:50 -08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Build a representation for an object for logging | 
					
						
							|  |  |  |      * @param mixed | 
					
						
							|  |  |  |      * @return string | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function logrep($object) { | 
					
						
							|  |  |  |         if (is_object($object)) { | 
					
						
							|  |  |  |             $class = get_class($object); | 
					
						
							|  |  |  |             if (isset($object->id)) { | 
					
						
							|  |  |  |                 return "$class $object->id"; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             return $class; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (is_string($object)) { | 
					
						
							|  |  |  |             $len = strlen($object); | 
					
						
							|  |  |  |             $fragment = mb_substr($object, 0, 32); | 
					
						
							|  |  |  |             if (mb_strlen($object) > 32) { | 
					
						
							|  |  |  |                 $fragment .= '...'; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             return "string '$fragment' ($len bytes)"; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return strval($object); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Encode an object for queued storage. | 
					
						
							|  |  |  |      * Next gen may use serialization. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param mixed $object | 
					
						
							|  |  |  |      * @return string | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     protected function encode($object) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if ($object instanceof Notice) { | 
					
						
							|  |  |  |             return $object->id; | 
					
						
							|  |  |  |         } else if (is_string($object)) { | 
					
						
							|  |  |  |             return $object; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             throw new ServerException("Can't queue this type", 500); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Decode an object from queued storage. | 
					
						
							|  |  |  |      * Accepts back-compat notice reference entries and strings for now. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param string | 
					
						
							|  |  |  |      * @return mixed | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     protected function decode($frame) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (is_numeric($frame)) { | 
					
						
							|  |  |  |             return Notice::staticGet(intval($frame)); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             return $frame; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-12 19:57:15 -08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Instantiate the appropriate QueueHandler class for the given queue. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param string $queue | 
					
						
							|  |  |  |      * @return mixed QueueHandler or null | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function getHandler($queue) | 
					
						
							| 
									
										
										
										
											2009-06-28 14:38:31 -04:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2010-01-12 19:57:15 -08:00
										 |  |  |         if (isset($this->handlers[$queue])) { | 
					
						
							|  |  |  |             $class = $this->handlers[$queue]; | 
					
						
							|  |  |  |             if (class_exists($class)) { | 
					
						
							|  |  |  |                 return new $class(); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 common_log(LOG_ERR, "Nonexistent handler class '$class' for queue '$queue'"); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             common_log(LOG_ERR, "Requested handler for unkown queue '$queue'"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							| 
									
										
										
										
											2010-01-21 16:42:50 -08:00
										 |  |  |      * Get a list of registered queue transport names to be used | 
					
						
							|  |  |  |      * for this daemon. | 
					
						
							| 
									
										
										
										
											2010-01-12 19:57:15 -08:00
										 |  |  |      * | 
					
						
							|  |  |  |      * @return array of strings | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function getQueues() | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2010-01-21 16:42:50 -08:00
										 |  |  |         $group = $this->activeGroup(); | 
					
						
							|  |  |  |         return array_keys($this->groups[$group]); | 
					
						
							| 
									
										
										
										
											2010-01-12 19:57:15 -08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Initialize the list of queue handlers | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @event StartInitializeQueueManager | 
					
						
							|  |  |  |      * @event EndInitializeQueueManager | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function initialize() | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2010-01-21 16:42:50 -08:00
										 |  |  |         // @fixme we'll want to be able to listen to particular queues...
 | 
					
						
							| 
									
										
										
										
											2010-01-12 19:57:15 -08:00
										 |  |  |         if (Event::handle('StartInitializeQueueManager', array($this))) { | 
					
						
							| 
									
										
										
										
											2010-01-21 16:42:50 -08:00
										 |  |  |             $this->connect('plugin', 'PluginQueueHandler'); | 
					
						
							|  |  |  |             $this->connect('omb', 'OmbQueueHandler'); | 
					
						
							|  |  |  |             $this->connect('ping', 'PingQueueHandler'); | 
					
						
							| 
									
										
										
										
											2010-01-25 18:08:21 -05:00
										 |  |  |             $this->connect('distrib', 'DistribQueueHandler'); | 
					
						
							| 
									
										
										
										
											2010-01-21 16:42:50 -08:00
										 |  |  |             if (common_config('sms', 'enabled')) { | 
					
						
							|  |  |  |                 $this->connect('sms', 'SmsQueueHandler'); | 
					
						
							| 
									
										
										
										
											2010-01-12 19:57:15 -08:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // XMPP output handlers...
 | 
					
						
							| 
									
										
										
										
											2010-01-21 16:42:50 -08:00
										 |  |  |             $this->connect('jabber', 'JabberQueueHandler'); | 
					
						
							|  |  |  |             $this->connect('public', 'PublicQueueHandler'); | 
					
						
							|  |  |  |             // @fixme this should get an actual queue
 | 
					
						
							|  |  |  |             //$this->connect('confirm', 'XmppConfirmHandler');
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // For compat with old plugins not registering their own handlers.
 | 
					
						
							|  |  |  |             $this->connect('plugin', 'PluginQueueHandler'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             $this->connect('xmppout', 'XmppOutQueueHandler', 'xmppdaemon'); | 
					
						
							| 
									
										
										
										
											2010-01-12 19:57:15 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2010-01-21 16:42:50 -08:00
										 |  |  |         Event::handle('EndInitializeQueueManager', array($this)); | 
					
						
							| 
									
										
										
										
											2010-01-12 19:57:15 -08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Register a queue transport name and handler class for your plugin. | 
					
						
							|  |  |  |      * Only registered transports will be reliably picked up! | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param string $transport | 
					
						
							|  |  |  |      * @param string $class | 
					
						
							| 
									
										
										
										
											2010-01-21 16:42:50 -08:00
										 |  |  |      * @param string $group | 
					
						
							| 
									
										
										
										
											2010-01-12 19:57:15 -08:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2010-01-21 16:42:50 -08:00
										 |  |  |     public function connect($transport, $class, $group='queuedaemon') | 
					
						
							| 
									
										
										
										
											2010-01-12 19:57:15 -08:00
										 |  |  |     { | 
					
						
							|  |  |  |         $this->handlers[$transport] = $class; | 
					
						
							| 
									
										
										
										
											2010-01-21 16:42:50 -08:00
										 |  |  |         $this->groups[$group][$transport] = $class; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * @return string queue group to use for this request | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function activeGroup() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $group = 'queuedaemon'; | 
					
						
							|  |  |  |         if ($this->master) { | 
					
						
							|  |  |  |             // hack hack
 | 
					
						
							|  |  |  |             if ($this->master instanceof XmppMaster) { | 
					
						
							|  |  |  |                 return 'xmppdaemon'; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return $group; | 
					
						
							| 
									
										
										
										
											2010-01-12 19:57:15 -08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Send a statistic ping to the queue monitoring system, | 
					
						
							|  |  |  |      * optionally with a per-queue id. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param string $key | 
					
						
							|  |  |  |      * @param string $queue | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function stats($key, $queue=false) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $owners = array(); | 
					
						
							|  |  |  |         if ($queue) { | 
					
						
							|  |  |  |             $owners[] = "queue:$queue"; | 
					
						
							|  |  |  |             $owners[] = "site:" . common_config('site', 'server'); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (isset($this->master)) { | 
					
						
							|  |  |  |             $this->master->stats($key, $owners); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             $monitor = new QueueMonitor(); | 
					
						
							|  |  |  |             $monitor->stats($key, $owners); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2009-07-01 12:09:18 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2009-06-28 14:38:31 -04:00
										 |  |  | } |