From: Roland Haeder Date: Tue, 24 Mar 2015 20:10:57 +0000 (+0100) Subject: Added task handler from 'hub' project. X-Git-Url: https://git.mxchange.org/?p=core.git;a=commitdiff_plain;h=f481782bb05334d3e6fcab65560239b61086bfa1 Added task handler from 'hub' project. Signed-off-by: Roland Häder --- diff --git a/inc/classes/main/handler/class_BaseHandler.php b/inc/classes/main/handler/class_BaseHandler.php new file mode 100644 index 00000000..e9512006 --- /dev/null +++ b/inc/classes/main/handler/class_BaseHandler.php @@ -0,0 +1,78 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2015 Core Developer Team + * @license GNU GPL 3.0 or any newer version + * @link http://www.shipsimu.org + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +class BaseHandler extends BaseFrameworkSystem implements HandleableDataSet { + /** + * Handler name + */ + private $handlerName = 'invalid'; + + /** + * Protected constructor + * + * @param $className Name of the class + * @return void + */ + protected function __construct ($className) { + // Call parent constructor + parent::__construct($className); + } + + /** + * Getter for handler name + * + * @return $handlerName Name of this handler + */ + public final function getHandlerName () { + return $this->handlerName; + } + + /** + * Setter for handler name + * + * @param $handlerName Name of this handler + * @return void + */ + protected final function setHandlerName ($handlerName) { + $this->handlerName = $handlerName; + } + + /** + * Adds all required elements from given array into data set instance + * + * @param $dataSetInstance An instance of a StoreableCriteria class + * @param $messageData An array with all message data + * @return void + * @todo Rewrite this to use DHT + */ + public function addArrayToDataSet (StoreableCriteria $dataSetInstance, array $messageData) { + // Add some generic data all messageData arrays provide + /* + $dataSetInstance->addCriteria(NodeListDatabaseWrapper::DB_COLUMN_ANSWER_STATUS, $messageData[BaseXmlAnswerTemplateEngine::ANSWER_STATUS]); + $dataSetInstance->addCriteria(NodeListDatabaseWrapper::DB_COLUMN_MESSAGE_TYPE , $messageData[NetworkPackage::MESSAGE_ARRAY_TYPE]); + */ + } +} + +// [EOF] +?> diff --git a/inc/classes/main/handler/tasks/.htaccess b/inc/classes/main/handler/tasks/.htaccess new file mode 100644 index 00000000..3a428827 --- /dev/null +++ b/inc/classes/main/handler/tasks/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/inc/classes/main/handler/tasks/class_TaskHandler.php b/inc/classes/main/handler/tasks/class_TaskHandler.php new file mode 100644 index 00000000..9fd650a6 --- /dev/null +++ b/inc/classes/main/handler/tasks/class_TaskHandler.php @@ -0,0 +1,335 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2015 Core Developer Team + * @license GNU GPL 3.0 or any newer version + * @link http://www.shipsimu.org + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +class TaskHandler extends BaseHandler implements Registerable, HandleableTask { + // Exception constants + const EXCEPTION_TASK_IS_INVALID = 0xb00; + + /** + * Protected constructor + * + * @return void + */ + protected function __construct () { + // Call parent constructor + parent::__construct(__CLASS__); + + // Set handler name + $this->setHandlerName('task'); + } + + /** + * Creates an instance of this class + * + * @return $handlerInstance An instance of a HandleableTask class + */ + public static final function createTaskHandler () { + // Get new instance + $handlerInstance = new TaskHandler(); + + // Output debug message + self::createDebugInstance(__CLASS__)->debugOutput('TASK-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Initializing task handler.'); + + // Init the task list + $handlerInstance->setListInstance(ObjectFactory::createObjectByConfiguredName('task_list_class')); + + // Get default instance + $handlerInstance->setIteratorInstance($handlerInstance->getListInstance()->getIterator()); + + // Init visitor instance for faster loop + $handlerInstance->setVisitorInstance(ObjectFactory::createObjectByConfiguredName('active_task_visitor_class')); + + // Register the first (and generic) idle-loop task + $taskInstance = ObjectFactory::createObjectByConfiguredName('idle_task_class'); + $handlerInstance->registerTask('idle_loop', $taskInstance); + + // Output debug message + self::createDebugInstance(__CLASS__)->debugOutput('TASK-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Task handler initialized.'); + + // Return the prepared instance + return $handlerInstance; + } + + /** + * Tries to execute the given task. If as task should not be started (yet) + * or the interval time (see task_interval_delay) is not yet reached the + * task is quietly skipped. + * + * @return void + * @throws InvalidTaskException If the current task is invalid + */ + private function executeCurrentTask () { + // Update no task by default + $updateTask = FALSE; + + // Is the current task valid? + if (!$this->getListInstance()->getIterator()->valid()) { + // Not valid! + throw new InvalidTaskException($this, self::EXCEPTION_TASK_IS_INVALID); + } // END - if + + // Get current task + $currentTask = $this->getListInstance()->getIterator()->current(); + + // Is the task not yet started? + if ($currentTask['task_started'] === FALSE) { + // Determine difference between current time and registration + $diff = ($this->getMilliTime() - $currentTask['task_registered']) * 1000; + + // Should we start now? + if ($diff < $currentTask['task_startup_delay']) { + // Skip this silently + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('TASK-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Task ' . $currentTask['id'] . ' not started: diff=' . $diff . ',task_startup_delay=' . $currentTask['task_startup_delay']); + return; + } // END - if + + // Launch the task and mark it as updated + $currentTask['task_started'] = TRUE; + $updateTask = TRUE; + + // Debug message + self::createDebugInstance(__CLASS__)->debugOutput('TASK-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Task ' . $currentTask['id'] . ' started with startup_delay=' . $currentTask['task_startup_delay'] . 'ms'); + } // END - if + + // Get time difference from interval delay + $diff = ($this->getMilliTime() - $currentTask['task_last_activity']) * 1000; + + // Is the interval delay reached? + if ((($diff < $currentTask['task_interval_delay']) && ($currentTask['task_max_runs'] == 0)) || (($currentTask['task_total_runs'] == $currentTask['task_max_runs']) && ($currentTask['task_max_runs'] > 0))) { + // Should we update the task from startup? + if ($updateTask === TRUE) { + // Update the task before leaving + $this->updateTask($currentTask); + } // END - if + + // Skip this silently + return; + } // END - if + + // Set last activity + $currentTask['task_last_activity'] = $this->getMilliTime(); + + // Count this run + $currentTask['task_total_runs']++; + + // Update the task + $this->updateTask($currentTask); + + // And visit/run it + // @TODO Messurement can be added around this call + $currentTask['task_instance']->accept($this->getVisitorInstance()); + } + + /** + * Updates given task by updating the underlaying list + * + * @param $taskEntry An array with a task + * @return void + */ + private function updateTask (array $taskEntry) { + // Get the key from current iteration + $key = $this->getListInstance()->getIterator()->key(); + + // Get the hash from key + $hash = $this->getListInstance()->getHash($key); + + // Update the entry + $this->getListInstance()->updateCurrentEntryByHash($hash, $taskEntry); + } + + /** + * Unregisters the given task + * + * @param $taskData Data of the task + * @return void + */ + private function unregisterTask (array $taskData) { + // Debug output + self::createDebugInstance(__CLASS__)->debugOutput('TASK-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Removing task ' . $taskData['id'] . ' from queue - CALLED!'); + + // Remove the entry + $this->getListInstance()->removeEntry('tasks', $taskData); + + // Debug output + self::createDebugInstance(__CLASS__)->debugOutput('TASK-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Removing task ' . $taskData['id'] . ' from queue - EXIT!'); + } + + /** + * Searches a task by given instance + * + * @param $taskInstanc An instanceof a Taskable class + * @return $taskName Name of the task as used while registration + */ + public function searchTask (Taskable $taskInstance) { + // Default is an empty (not found) task name + $taskName = ''; + + // Get whole list + $taskList = $this->getListInstance()->getArrayFromList('tasks'); + + // Search all instances + foreach ($taskList as $currentTask) { + // Does it match given task instance? + if ($currentTask['task_instance']->equals($taskInstance)) { + // Found it + $taskName = $currentTask['id']; + + // Abort here + break; + } // END - if + } // END - foreach + + // Return found name + return $taskName; + } + + /** + * Registers a task with a task handler. + * + * @param $taskName A task name to register the task on + * @param $taskInstance The instance we should register as a task + * @return void + */ + public function registerTask ($taskName, Visitable $taskInstance) { + // Create the entry + $taskEntry = array( + // Identifier for the generateHash() method + 'id' => $taskName, + // Whether the task is started + 'task_started' => FALSE, + // Whether the task is paused (not yet implemented) + 'task_paused' => FALSE, + // Whether the task can be paused (not yet implemented) + 'task_pauseable' => TRUE, + // Timestamp of registration + 'task_registered' => $this->getMilliTime(), + // Last activity timestamp + 'task_last_activity' => 0, + // Total runs of this task + 'task_total_runs' => 0, + // Task instance itself + 'task_instance' => $taskInstance, + // Startup delay in milliseconds + 'task_startup_delay' => $this->getConfigInstance()->getConfigEntry('task_' . $taskName . '_startup_delay'), + // Interval time (delay) in milliseconds before this task is executed again + 'task_interval_delay' => $this->getConfigInstance()->getConfigEntry('task_' . $taskName . '_interval_delay'), + // How often should this task run? + 'task_max_runs' => $this->getConfigInstance()->getConfigEntry('task_' . $taskName . '_max_runs'), + ); + + // Add the entry + $this->getListInstance()->addEntry('tasks', $taskEntry); + + // Debug message + self::createDebugInstance(__CLASS__)->debugOutput('TASK-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Task registered: taskName=' . $taskName . + ' (taskInstance=' . $taskInstance->__toString() . ')' . + ', startupDelay=' . $taskEntry['task_startup_delay'] . 'ms' . + ', intervalDelay=' . $taskEntry['task_interval_delay'] . 'ms' . + ', maxRuns=' . $taskEntry['task_max_runs'] . ' ...' + ); + } + + /** + * Checks whether tasks are left including idle task + * + * @return $tasksLeft Whether there are tasks left to handle + */ + public function hasTasksLeft () { + // Do we have tasks there? + $tasksLeft = (($this->getListInstance() instanceof Listable) && ($this->getListInstance()->count() > 0)); + + // Return result + return $tasksLeft; + } + + /** + * Handles all tasks by checking if they should startup or if it is their + * turn to run. You should use this method in a while() loop in conjuntion + * with hasTasksLeft() so you can e.g. shutdown by adding a ShutdownTask + * which will attempt to remove all tasks from the task handler. + * + * @return void + */ + public function handleTasks () { + // Should we rewind? + if (!$this->getListInstance()->getIterator()->valid()) { + // Rewind to the beginning for next loop + $this->getListInstance()->getIterator()->rewind(); + } // END - if + + // Try to execute the task + $this->executeCurrentTask(); + + // Go to next entry + $this->getListInstance()->getIterator()->next(); + } + + /** + * Shuts down all tasks and the task handler itself. This method should be + * called from a corresponding filter class. + * + * @return void + */ + public function doShutdown () { + // Always rewind to the beginning for next loop + $this->getListInstance()->getIterator()->rewind(); + + // Debug message + self::createDebugInstance(__CLASS__)->debugOutput('TASK-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Shutting down all ' . $this->getListInstance()->count() . ' tasks...'); + + // Remember all tasks that has been shutdown for removal + $tasks = array(); + + // Instance a visitor + $this->setVisitorInstance(ObjectFactory::createObjectByConfiguredName('shutdown_task_visitor_class')); + + // Shutdown all tasks in once go + while ($this->getListInstance()->getIterator()->valid()) { + // Get current entry + $currentTask = $this->getListInstance()->getIterator()->current(); + + // Output debug message + self::createDebugInstance(__CLASS__)->debugOutput('TASK-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Shutting down task ' . $currentTask['id'] . ' (taskInstance=' . $currentTask['task_instance']->__toString() . ') ...'); + + // Shutdown the task + $currentTask['task_instance']->accept($this->getVisitorInstance()); + + // Remember this task + array_push($tasks, $currentTask); + + // Advance to next one + $this->getListInstance()->getIterator()->next(); + } // END - while + + // Debug message + self::createDebugInstance(__CLASS__)->debugOutput('TASK-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Shutdown of all tasks completed.'); + + // Remove all tasks + foreach ($tasks as $entry) { + $this->unregisterTask($entry); + } // END - foreach + } +} + +// [EOF] +?>