ActionQueue: handle case of missing user object
[tine20] / tine20 / Tinebase / ActionQueue.php
1 <?php
2 /**
3  * Tine 2.0
4  * 
5  * @package     Tinebase
6  * @subpackage  ActionQueue
7  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
8  * @author      Cornelius Weiss <c.weiss@metaways.de>
9  * @copyright   Copyright (c) 2009-2013 Metaways Infosystems GmbH (http://www.metaways.de)
10  */
11
12 /**
13  * Action Queue
14  * 
15  * Method queue for deferred/async execution of Tine 2.0 application actions as defined 
16  * in the application controllers 
17  *
18  * @package     Tinebase
19  * @subpackage  ActionQueue
20  */
21  class Tinebase_ActionQueue implements Tinebase_Controller_Interface
22  {
23      const BACKEND_DIRECT = 'Direct';
24      const BACKEND_REDIS  = 'Redis';
25      
26      /**
27       * holds queue instance
28       * 
29       * @var Zend_Queue
30       */
31      protected $_queue = NULL;
32      
33     /**
34      * holds the instance of the singleton
35      *
36      * @var Tinebase_ActionQueue
37      */
38     private static $_instance = NULL;
39
40     /**
41      * don't clone. Use the singleton.
42      *
43      */
44     private function __clone() 
45     {
46     }
47     
48     /**
49      * the singleton pattern
50      *
51      * @return Tinebase_ActionQueue
52      */
53     public static function getInstance() 
54     {
55         if (self::$_instance === NULL) {
56             self::$_instance = new Tinebase_ActionQueue();
57         }
58         
59         return self::$_instance;
60     }
61     
62     /**
63      * destroy instance of this class
64      */
65     public static function destroyInstance()
66     {
67         self::$_instance = NULL;
68     }
69     
70     /**
71      * constructor
72      */
73     private function __construct()
74     {
75         $options = null;
76         $backend = self::BACKEND_DIRECT;
77         
78         if (isset(Tinebase_Core::getConfig()->actionqueue) && Tinebase_Core::getConfig()->actionqueue->active) {
79             $options = Tinebase_Core::getConfig()->actionqueue->toArray();
80             
81             $backend = (isset($options['backend']) || array_key_exists('backend', $options)) ? ucfirst(strtolower($options['backend'])) : $backend;
82             unset($options['backend']);
83         }
84         
85         $className = 'Tinebase_ActionQueue_Backend_' . $backend;
86         
87         if (!class_exists($className)) {
88             if (Tinebase_Core::isLogLevel(Zend_Log::ERR)) Tinebase_Core::getLogger()->err(
89                 __METHOD__ . '::' . __LINE__ . " Queue class name {$className} not found. Falling back to direct execution.");
90             
91             $className = 'Tinebase_ActionQueue_Backend_Direct';
92         }
93     
94         $this->_queue = new $className($options); 
95
96         if (! $this->_queue instanceof Tinebase_ActionQueue_Backend_Interface) {
97             throw new Tinebase_Exception_UnexpectedValue('backend does not implement Tinebase_ActionQueue_Backend_Interface');
98         }
99     }
100     
101     /**
102      * execute action defined in queue message
103      * 
104      * @param  array  $message  action
105      * @return mixed
106      */
107     public function executeAction($message)
108     {
109         if (! is_array($message) || ! (isset($message['action']) || array_key_exists('action', $message)) || strpos($message['action'], '.') === FALSE) {
110             throw new Tinebase_Exception_NotFound('Could not execute action, invalid message/action param');
111         }
112
113         if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(
114             __METHOD__ . '::' . __LINE__ . " executing action: '{$message['action']}'");
115         
116         list($appName, $actionName) = explode('.', $message['action']);
117         $controller = Tinebase_Core::getApplicationInstance($appName);
118     
119         if (! method_exists($controller, $actionName)) {
120             throw new Tinebase_Exception_NotFound('Could not execute action, requested action does not exist');
121         }
122         
123         return call_user_func_array(array($controller, $actionName), $message['params']);
124     }
125     
126     /**
127      * check if the backend is async
128      *  
129      * @return boolean true if queue backend is async
130      */
131     public function hasAsyncBackend()
132     {
133         return ! $this->_queue instanceof Tinebase_ActionQueue_Backend_Direct;
134     }
135     
136     /**
137      * process all jobs in queue
138      */
139     public function processQueue()
140     {
141         // loop over all jobs
142         while($jobId = Tinebase_ActionQueue::getInstance()->waitForJob()) {
143             $job = $this->receive($jobId);
144             
145             $this->executeAction($job);
146             
147             $this->delete($jobId);
148         }
149     }
150     
151     /**
152      * queues an action
153      * 
154      * @param string $_action
155      * @param mixed  $_arg1
156      * @param mixed  $_arg2
157      * ...
158      * 
159      * @return string the job id
160      */
161     public function queueAction()
162     {
163         $params = func_get_args();
164         $action = array_shift($params);
165         $user = Tinebase_Core::getUser();
166         if (! is_object($user)) {
167             if (Tinebase_Core::isLogLevel(Zend_Log::ERR)) Tinebase_Core::getLogger()->err(
168                 __METHOD__ . '::' . __LINE__ . " Not Queueing action: '{$action}' because no valid user object found");
169             return null;
170         }
171
172         $message = array(
173             'action'     => $action,
174             'account_id' => $user->getId(),
175             'params'     => $params
176         );
177         
178         if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(
179             __METHOD__ . '::' . __LINE__ . " Queueing action: '{$action}'");
180         
181         return $this->_queue->send($message);
182     }
183     
184     /**
185      * resume processing of events
186      */
187     public function resumeEvents()
188     {
189     }
190     
191     /**
192      * suspend processing of event
193      */
194     public function suspendEvents()
195     {
196     }
197
198     /**
199      * call function of queue backend
200      * 
201      * @param  string $name
202      * @param  array  $arguments
203      * @return mixed
204      */
205     public function __call($name, $arguments)
206     {
207         return call_user_func_array(array($this->_queue, $name), $arguments);
208     }
209
210      /**
211       * returns the class name of the used queue implementation
212       *
213       * @return string
214       */
215     public function getBackendType()
216     {
217         return get_class($this->_queue);
218     }
219 }