--- /dev/null
+<?php
+/**
+ * A general Handler
+ *
+ * @author Roland Haeder <webmaster@shipsimu.org>
+ * @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 <http://www.gnu.org/licenses/>.
+ */
+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]
+?>
--- /dev/null
+<?php
+/**
+ * A Task handler
+ *
+ * @author Roland Haeder <webmaster@shipsimu.org>
+ * @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 <http://www.gnu.org/licenses/>.
+ */
+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]
+?>