PageRenderTime 58ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/src/object/task_manager/DefaultTaskManager.class.php

https://bitbucket.org/stk2k/charcoalphp2.1
PHP | 405 lines | 240 code | 61 blank | 104 comment | 28 complexity | 1b92940e42dc1b0e604b0c34b77ab6ac MD5 | raw file
  1. <?php
  2. /**
  3. * simple task manager
  4. *
  5. * PHP version 5
  6. *
  7. * @package objects.task_managers
  8. * @author CharcoalPHP Development Team
  9. * @copyright 2008 stk2k, sazysoft
  10. */
  11. class Charcoal_DefaultTaskManager extends Charcoal_AbstractTaskManager
  12. {
  13. const TAG = 'charcoal.object.default_task_manager';
  14. /** @var Charcoal_Itask[] */
  15. private $tasks;
  16. /** @var Charcoal_EventQueue */
  17. private $queue;
  18. /** @var integer */
  19. private $max_event_loop;
  20. /**
  21. * Initialize instance
  22. *
  23. * @param array $config configuration data
  24. */
  25. public function configure( $config )
  26. {
  27. parent::configure( $config );
  28. $config = new Charcoal_HashMap($config);
  29. $this->max_event_loop = ui( $config->getInteger( 'max_event_loop', 1000 ) );
  30. $this->tasks = new Charcoal_Vector();
  31. $this->queue = new Charcoal_EventQueue();
  32. }
  33. /**
  34. * refister task
  35. *
  36. * @param Charcoal_String|string $key
  37. * @param Charcoal_ITask $task
  38. */
  39. public function registerTask( $key, $task )
  40. {
  41. $key = us( $key );
  42. if ( isset($this->tasks[$key]) ){
  43. log_warning( "system,event", "task[$key] is already registered." );
  44. return;
  45. }
  46. // save task in the map
  47. $this->tasks[$key] = $task;
  48. log_debug( 'system,event', "registered task[$task] as [$key]" );
  49. }
  50. /**
  51. * unregister task
  52. *
  53. * @param Charcoal_String|string $key
  54. */
  55. public function unregisterTask( $key )
  56. {
  57. $key = us( $key );
  58. // remove task from the map
  59. unset( $this->tasks[$key] );
  60. }
  61. /**
  62. * test if task is registered
  63. *
  64. * @param Charcoal_String|string $key
  65. *
  66. * @return boolean TRUE if a task is registered, otherwise FALSE
  67. */
  68. public function isTaskRegistered( $key )
  69. {
  70. $key = us( $key );
  71. return isset($this->tasks[$key]);
  72. }
  73. /**
  74. * get task
  75. *
  76. * @param Charcoal_String|string $key
  77. *
  78. * @return Charcoal_ITask
  79. *
  80. * @throws Charcoal_TaskNotFoundException
  81. */
  82. public function getTask( $key )
  83. {
  84. $key = us( $key );
  85. if ( isset($this->tasks[$key]) ){
  86. return $this->tasks[$key];
  87. }
  88. throw new Charcoal_TaskNotFoundException( $key );
  89. }
  90. /*
  91. * Get event queue
  92. *
  93. * @return Charcoal_IEventQueue event queue object
  94. */
  95. public function getEventQueue()
  96. {
  97. return $this->queue;
  98. }
  99. /**
  100. * save statefull task
  101. *
  102. * @param Charcoal_Session $session
  103. */
  104. public function saveStatefulTasks( $session )
  105. {
  106. //print "saveStatefulTasks<br>";
  107. foreach( $this->tasks as $task ){
  108. if ( $task instanceof Charcoal_IStateful ){
  109. /** @var Charcoal_ITask|Charcoal_IStateful $task */
  110. $data_id = "task://" . $task->getObjectPath();
  111. $session->set( $data_id, $task->serializeContents() );
  112. }
  113. }
  114. }
  115. /**
  116. * restore stateful task
  117. *
  118. * @param Charcoal_Session $session
  119. */
  120. public function restoreStatefulTasks( $session )
  121. {
  122. //print "restoreStatefulTasks<br>";
  123. foreach( $this->tasks as $task ){
  124. if ( $task instanceof Charcoal_IStateful ){
  125. /** @var Charcoal_ITask|Charcoal_IStateful $task */
  126. $data_id = "task://" . $task->getObjectPath();
  127. if ( isset($_SESSION[ $data_id ]) ){
  128. $data = $_SESSION[ $data_id ];
  129. $task->deserializeContents( unserialize($data) );
  130. }
  131. else{
  132. // デシリアライズされなかった場合は初期化する
  133. $task->initContents();
  134. }
  135. }
  136. }
  137. }
  138. /*
  139. * add an event to task manager
  140. *
  141. * @param Charcoal_IEvent $event
  142. */
  143. public function pushEvent( $event )
  144. {
  145. // add an event to event queue
  146. $this->queue->enqueue( $event );
  147. log_debug( 'system,event', "event[$event] was enqueued." );
  148. }
  149. /**
  150. * process events
  151. *
  152. * @param Charcoal_IEventContext $context
  153. *
  154. * @return int
  155. *
  156. * @throws Charcoal_BusinessException|Charcoal_RuntimeException
  157. */
  158. public function processEvents( $context )
  159. {
  160. log_debug( 'system,event', "processEvents start." );
  161. $max_event_loop = $this->max_event_loop;
  162. $exit_code = 0;
  163. try{
  164. $queue = $this->queue;
  165. $loop_id = 0;
  166. while( !$queue->isEmpty() )
  167. {
  168. log_debug( 'system,event', "event queue(" . count($queue) . "): $queue");
  169. // increment loop counter
  170. $loop_id ++;
  171. /** @var Charcoal_IEvent $event */
  172. $event = $queue->dequeue();
  173. /** @var string $event_name */
  174. $event_name = $event->getObjectName();
  175. /** @var Charcoal_ObjectPath $event_id */
  176. $event_id = $event->getObjectPath();
  177. $delete_event = FALSE;
  178. $context->setEvent( $event );
  179. // if this event loop exceeds [max_event_loop], thro exception
  180. if ( $loop_id > $max_event_loop ){
  181. log_warning( "system,event", "[loop:$loop_id/$event_name] aborting by overflow maximum loop count[$max_event_loop].", "task_manager" );
  182. log_warning( "system,event", "[loop:$loop_id/$event_name] event queue=[$queue].", "task_manager" );
  183. _throw( new Charcoal_EventLoopCounterOverflowException( $max_event_loop ) );
  184. }
  185. log_debug( 'system,event', "[loop:$loop_id/$event_name] event loop start.");
  186. // タスク一覧を優先度でソートする
  187. $key_priority = array();
  188. foreach ( $this->tasks as $key => $task ){
  189. $key_priority[$key] = ui( $task->getPriority() );
  190. }
  191. $a_task_list = uv($this->tasks);
  192. array_multisort( $key_priority,SORT_DESC, $a_task_list );
  193. $this->tasks = v($a_task_list);
  194. // task list to remove on end of this loop
  195. $remove_tasks = NULL;
  196. // すべてのタスクにイベントをディスパッチする
  197. log_debug( 'system,event', "[loop:$loop_id/$event_name] task list: [$this->tasks]" );
  198. foreach( $this->tasks as $task )
  199. {
  200. $task_name = $task->getObjectName();
  201. $task_id = $task->getObjectPath();
  202. log_debug( 'system,event', "[loop:$loop_id/$event_name/$task_name] event[$event_name] is dispatching to task[$task_name].");
  203. // イベントフィルタ
  204. $process = FALSE;
  205. $event_filters = $task->getEventFilters();
  206. log_debug( 'system,event', "[loop:$loop_id/$event_name/$task_name] task event filter: " . $event_filters );
  207. foreach( $event_filters as $filter ){
  208. if ( $event_id->getObjectPathString() == us($filter) ){
  209. $process = TRUE;
  210. break;
  211. }
  212. }
  213. if ( !$process ){
  214. log_debug( 'system,event', "[loop:$loop_id/$event_name/$task_name] event[$event_name] is NOT found in task's event filters: [$event_filters]. Passing this task.");
  215. continue;
  216. }
  217. log_debug( 'system,event', "[loop:$loop_id/$event_name/$task_name] event[$event_name] is found in task's event filters: [$event_filters].");
  218. // process event
  219. $result = NULL;
  220. try{
  221. $result = $task->processEvent( $context );
  222. log_debug( 'system,event', "[loop:$loop_id/$event_name/$task_name] returned from processEvent with result:" . print_r($result,true) );
  223. }
  224. catch( Charcoal_BusinessException $e )
  225. {
  226. // just handle the exception
  227. $exception_handled = $task->handleException( $e, $context );
  228. if ( b($exception_handled)->isFalse() ){
  229. // just re-throw the exception, if the exception was not handled by the task
  230. throw( $e );
  231. }
  232. }
  233. catch( Exception $e )
  234. {
  235. // write log and handle the exception
  236. _catch( $e );
  237. $exception_handled = $task->handleException( $e, $context );
  238. if ( b($exception_handled)->isFalse() ){
  239. // write log and re-throw the exception, if the exception was not handled by the task
  240. _throw( $e );
  241. }
  242. }
  243. // result value handling
  244. $result_str = NULL;
  245. if ( $result === NULL ){
  246. $result_str = 'NULL';
  247. }
  248. elseif ( $result === FALSE || ($result instanceof Charcoal_Boolean) && $result->isFalse() ){
  249. $result_str = 'FALSE';
  250. $result = FALSE;
  251. }
  252. elseif ( $result === TRUE || ($result instanceof Charcoal_Boolean) && $result->isTrue() ){
  253. $result_str = 'TRUE';
  254. $result = TRUE;
  255. }
  256. else{
  257. $msg = "processEvent() must return a [boolean] value. but returned:" . print_r($result,true);
  258. log_error( 'system,event,error', $msg, self::TAG );
  259. _throw( new Charcoal_ProcessEventAtTaskException( $event, $task, $result, $msg ) );
  260. }
  261. // end of processing event
  262. log_debug( 'system,event', "[loop:$loop_id/$event_name] event was processed by task[$task_name]. result=[$result_str]" );
  263. // process post action
  264. $post_actions = $task->getPostActions();
  265. log_debug( 'system,event', "[loop:$loop_id/$event_name/$task_name] task post actions: $post_actions" );
  266. if ( $result !== FALSE && $post_actions )
  267. {
  268. foreach( $post_actions as $key => $action )
  269. {
  270. $target = NULL;
  271. $action = us($action);
  272. if ( strpos(":",$action) !== FALSE ){
  273. list( $action, $target ) = explode( ":", trim($action) );
  274. log_debug( 'system,event', "[loop:$loop_id/$event_name/$task_name] post action[$action] with target[$target].");
  275. }
  276. else{
  277. $action = trim($action);
  278. log_debug( 'system,event', "[loop:$loop_id/$event_name/$task_name] post action[$action].");
  279. }
  280. switch( $action ){
  281. case "remove_task";
  282. // タスク実行リストからタスクを削除
  283. if ( !$target ){
  284. $target = $task_id;
  285. }
  286. if ( strcmp($target,$task_id) === 0 ){
  287. log_debug( 'system,event', "[loop:$loop_id/$event_name/$task_name] task[$target] is marked to remove." );
  288. $remove_tasks[] = $task_id;
  289. }
  290. break;
  291. case "remove_event":
  292. // イベントを削除
  293. if ( !$target ){
  294. $target = $event_id;
  295. }
  296. if ( strcmp($target,$event_id) === 0 ){
  297. log_debug( 'system,event', "[loop:$loop_id/$event_name/$task_name] event[$target] is marked to remove.");
  298. $delete_event |= TRUE;
  299. }
  300. break;
  301. case "continue_event":
  302. // イベントをキューに再投入
  303. break;
  304. }
  305. }
  306. }
  307. else{
  308. log_debug( 'system,event', "[loop:$loop_id/$event_name/$task_name] no post action is defined for event.");
  309. }
  310. log_debug( 'system,event', "[loop:$loop_id/$event_name] task loop end.");
  311. } // task loop end
  312. // remove tasks
  313. if ( $remove_tasks ){
  314. foreach( $remove_tasks as $task_id ){
  315. unset($this->tasks["$task_id"]);
  316. log_debug( 'system,event', "[loop:$loop_id/$event_name] removed task: $task_id" );
  317. }
  318. log_debug( 'system,event', "[loop:$loop_id/$event_name] next task list: [$this->tasks]" );
  319. }
  320. if ( !$delete_event ){
  321. // push back the event into our event queue
  322. $this->pushEvent( $event );
  323. }
  324. else{
  325. log_debug( 'system,event', "[loop:$loop_id/$event_name] event[$event] is removed." );
  326. }
  327. log_debug( 'system,event', "[loop:$loop_id/$event_name] event loop end.");
  328. } // event loop end
  329. if ( $queue->isEmpty() ){
  330. log_debug( 'system,event', "event queue is empty.");
  331. $exit_code = Charcoal_Event::EXIT_CODE_OK;
  332. }
  333. // ログ
  334. log_debug( 'system,event', "event loop end.");
  335. }
  336. catch( Charcoal_RuntimeException $e ){
  337. _catch( $e, true );
  338. log_debug( 'system,event,debug', "an exception occured while processing event." );
  339. _throw( new Charcoal_ProcessEventAtTaskManagerException( $e ) );
  340. }
  341. log_debug( 'system,event', "processEvents end: exit_code=" . print_r($exit_code,true) );
  342. return $exit_code;
  343. }
  344. }