From: Roland Häder Date: Fri, 19 May 2017 17:12:35 +0000 (+0200) Subject: Re-imported: X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=dbed653dbaa60319d8360ad615b306fab694a10d;p=hub.git Re-imported: - these classes/interfaces cannot be generalized, ConnectionHelper will break it then! - back-ported a lot stuff - fixed namespaces Signed-off-by: Roland Häder --- diff --git a/application/hub/classes/class_Base b/application/hub/classes/class_Base index f8439da71..65ff8cd26 100644 --- a/application/hub/classes/class_Base +++ b/application/hub/classes/class_Base @@ -1,4 +1,10 @@ listenerInstance = $listenerInstance; + } + + /** + * Getter for listener instance + * + * @return $listenerInstance A Listenable instance + */ + protected final function getListenerInstance () { + return $this->listenerInstance; + } + + /** + * Handles socket error for given socket resource and peer data. This method + * validates $socketResource if it is a valid resource (see is_resource()) + * but assumes valid data in array $recipientData, except that + * count($recipientData) is always 2. + * + * @param $method Value of __METHOD__ from calling method + * @param $line Value of __LINE__ from calling method + * @param $socketResource A valid socket resource + * @param $socketData A valid socket data array (0 = IP/file name, 1 = port) + * @return void + * @throws InvalidSocketException If $socketResource is no socket resource + * @throws NoSocketErrorDetectedException If socket_last_error() gives zero back + * @todo Move all this socket-related stuff into own class, most of it resides in BaseListener + */ + protected final function handleSocketError ($method, $line, $socketResource, array $socketData) { + // This method handles only socket resources + if (!is_resource($socketResource)) { + // No resource, abort here + throw new InvalidSocketException(array($this, $socketResource), BaseListener::EXCEPTION_INVALID_SOCKET); + } // END - if + + // Check socket array, 1st element is mostly IP address (or file name), 2nd is port number + //* DEBUG-DIE: */ die(__METHOD__ . ':socketData=' . print_r($socketData, true)); + assert(isset($socketData[0])); + assert(isset($socketData[1])); + + // Get error code for first validation (0 is not an error) + $errorCode = socket_last_error($socketResource); + + // If the error code is zero, someone called this method without an error + if ($errorCode == 0) { + // No error detected (or previously cleared outside this method) + throw new NoSocketErrorDetectedException(array($this, $socketResource), BaseListener::EXCEPTION_NO_SOCKET_ERROR); + } // END - if + + // Get handler (method) name + $handlerName = $this->getSocketErrorHandlerFromCode($errorCode); + + // Call-back the error handler method + call_user_func_array(array($this, $handlerName), array($socketResource, $socketData)); + + // Finally clear the error because it has been handled + socket_clear_error($socketResource); + } diff --git a/application/hub/classes/class_BaseHubSystem.php b/application/hub/classes/class_BaseHubSystem.php index 3e62ebed0..a84e33833 100644 --- a/application/hub/classes/class_BaseHubSystem.php +++ b/application/hub/classes/class_BaseHubSystem.php @@ -3,12 +3,12 @@ namespace Hub\Generic; // Import application-specific stuff +use Hub\Information\ShareableInfo; use Hub\Network\Receive\Receivable; +use Hub\Pool\Poolable; // Import framework stuff -use CoreFramework\Information\ShareableInfo; use CoreFramework\Object\BaseFrameworkSystem; -use CoreFramework\Pool\Poolable; /** * A general hub system class diff --git a/application/hub/classes/container/.htaccess b/application/hub/classes/container/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/application/hub/classes/container/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/application/hub/classes/container/socket/.htaccess b/application/hub/classes/container/socket/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/application/hub/classes/container/socket/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/application/hub/classes/container/socket/class_SocketContainer.php b/application/hub/classes/container/socket/class_SocketContainer.php new file mode 100644 index 000000000..f59d6fa8e --- /dev/null +++ b/application/hub/classes/container/socket/class_SocketContainer.php @@ -0,0 +1,136 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Hub 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 SocketContainer extends BaseContainer implements Registerable { + /** + * Protected constructor + * + * @return void + */ + protected function __construct () { + // Call parent constructor + parent::__construct(__CLASS__); + } + + /** + * Creates an instance of this Container class and prepares it for usage + * + * @param $socketResource A valid socket resource + * @param $infoInstance An instance of a ShareableInfo class + * @param $packageData Raw package data + * @return $containerInstance An instance of this Container class + */ + public static final function createSocketContainer ($socketResource, ShareableInfo $infoInstance = NULL, array $packageData = array()) { + // Get a new instance + $containerInstance = new SocketContainer(); + + // Remove unneeded entries + unset($packageData[NetworkPackage::PACKAGE_DATA_CONTENT]); + unset($packageData[NetworkPackage::PACKAGE_DATA_HASH]); + + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-CONTAINER[' . __METHOD__ . ':' . __LINE__ . ']:socketResource=' . $socketResource . ',packageData='.print_r($packageData, true)); + + // Is the info instance set? + if ($infoInstance instanceof ShareableInfo) { + // Get listener/helper from info class + $listenerInstance = $infoInstance->getListenerInstance(); + $helperInstance = $infoInstance->getHelperInstance(); + + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-CONTAINER[' . __METHOD__ . ':' . __LINE__ . ']: listenerInstance[]=' . gettype($listenerInstance)); + + // Is there a listener instance set? + if ($listenerInstance instanceof Listenable) { + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-CONTAINER[' . __METHOD__ . ':' . __LINE__ . ']: Setting listenerInstance=' . $listenerInstance->__toString() . ' ...'); + + // Set it here for later usage + $containerInstance->setListenerInstance($listenerInstance); + } elseif ($helperInstance instanceof ConnectionHelper) { + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-CONTAINER[' . __METHOD__ . ':' . __LINE__ . ']: Setting helperInstance=' . $helperInstance->__toString() . ' ...'); + + // Set it here for later usage + $containerInstance->setHelperInstance($helperInstance); + } + } // END - if + + // Set the resource ... + $containerInstance->setSocketResource($socketResource); + + // ... and package data + $containerInstance->setPackageData($packageData); + + // Return the prepared instance + return $containerInstance; + } + + /** + * Checks whether the given Universal Node Locator matches with the one from package data + * + * @param $unl A Universal Node Locator + * @return $matches Whether $address matches with the one from package data + */ + public final function ifAddressMatches ($unl) { + // Get current package data + $packageData = $this->getPackageData(); + + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-CONTAINER[' . __METHOD__ . ':' . __LINE__ . ']: unl=' . $unl . ',packageData=' . print_r($packageData, true)); + + // So, does both match? + $matches = ((isset($packageData[NetworkPackage::PACKAGE_DATA_RECIPIENT])) && ($packageData[NetworkPackage::PACKAGE_DATA_RECIPIENT] === $unl)); + + // Return result + return $matches; + } + + /** + * Checks whether the given socket matches with stored + * + * @param $socketResource A valid socket resource + * @return $matches Whether given socket matches + */ + public final function ifSocketResourceMatches ($socketResource) { + // Debug message + /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-CONTAINER[' . __METHOD__ . ':' . __LINE__ . ']: socketResource[' . gettype($socketResource) . ']=' .$socketResource . ',storedResource[' . gettype($this->getSocketResource()) . ']=' . $this->getSocketResource()); + + // So, does both match? + $matches = ((is_resource($socketResource)) && ($socketResource === $this->getSocketResource())); + + // Return result + return $matches; + } + +} diff --git a/application/hub/classes/handler/raw_data/.htaccess b/application/hub/classes/handler/raw_data/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/application/hub/classes/handler/raw_data/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/application/hub/classes/handler/raw_data/class_ b/application/hub/classes/handler/raw_data/class_ new file mode 100644 index 000000000..804c8e3c5 --- /dev/null +++ b/application/hub/classes/handler/raw_data/class_ @@ -0,0 +1,71 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Hub Developer Team + * @license GNU GPL 3.0 or any newer version + * @link http://www.ship-simu.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 ???NetworkPackageHandler extends BaseNetworkPackageHandler implements Networkable { + /** + * Protected constructor + * + * @return void + */ + protected function __construct () { + // Call parent constructor + parent::__construct(__CLASS__); + + // Set handler name + $this->setHandlerName('!!!'); + } + + /** + * Creates an instance of this class + * + * @return $handlerInstance An instance of a Networkable class + */ + public final static function create???NetworkPackageHandler () { + // Get new instance + $handlerInstance = new ???NetworkPackageHandler(); + + // Return the prepared instance + return $handlerInstance; + } + + /** + * Processes a package from given resource. This is mostly useful for TCP + * package handling and is implemented in the TcpListener class + * + * @param $resource A valid resource identifier + * @return void + * @throws InvalidResourceException If the given resource is invalid + * @todo 0% + */ + public function processResourcePackage ($resource) { + // Check the resource + if (!is_resource($resource)) { + // Throw an exception + throw new InvalidResourceException($this, self::EXCEPTION_INVALID_RESOURCE); + } // END - if + + // Implement processing here + $this->partialStub('Please implement this method.'); + } + +} diff --git a/application/hub/classes/handler/raw_data/class_BaseDataHandler.php b/application/hub/classes/handler/raw_data/class_BaseDataHandler.php new file mode 100644 index 000000000..275e7ec80 --- /dev/null +++ b/application/hub/classes/handler/raw_data/class_BaseDataHandler.php @@ -0,0 +1,203 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Hub 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 . + */ +abstract class BaseDataHandler extends BaseHandler { + /** + * Last exception instance from database layer or NULL (default) + */ + private $lastException = NULL; + + /** + * Array with search criteria elements + */ + protected $searchData = array(); + + /** + * Array with all data XML nodes (which hold the actual data) and their values + */ + protected $messageDataElements = array(); + + /** + * Array for translating message data elements (other node's data mostly) + * into configuration elements. + */ + protected $messageToConfig = array(); + + /** + * Array for copying configuration entries + */ + protected $configCopy = array(); + + /** + * Protected constructor + * + * @param $className Name of the class + * @return void + */ + protected function __construct ($className) { + // Call parent constructor + parent::__construct($className); + + // Get a DHT instance + $dhtInstance = DhtObjectFactory::createDhtInstance('node'); + + // Set it here + $this->setDhtInstance($dhtInstance); + } + + /** + * Getter for search data array + * + * @return $searchData Search data array + */ + public final function getSearchData () { + return $this->searchData; + } + + /** + * Getter for last exception + * + * @return $lastException Last thrown exception + */ + public final function getLastException () { + return $this->lastException; + } + + /** + * Setter for last exception + * + * @param $lastException Last thrown exception + * @return void + */ + public final function setLastException (FrameworkException $exceptionInstance = NULL) { + $this->lastException = $exceptionInstance; + } + + /** + * Prepares a message as answer for given message data for delivery. + * + * @param $messageData An array with all message data + * @param $packageInstance An instance of a Deliverable instance + * @return void + */ + protected function prepareAnswerMessage (array $messageData, Deliverable $packageInstance) { + // Debug message + /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('MESSAGE-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Going to send an answer message for ' . $this->getHandlerName() . ' ...'); + + // Get a helper instance based on this handler's name + $helperInstance = ObjectFactory::createObjectByConfiguredName('node_answer_' . $this->getHandlerName() . '_helper_class', array($messageData)); + + // Get node instance + $nodeInstance = NodeObjectFactory::createNodeInstance(); + + // Load descriptor XML + $helperInstance->loadDescriptorXml($nodeInstance); + + /* + * Set missing (temporary) configuration data, mostly it needs to be + * copied from message data array. + */ + $this->initMessageConfigurationData($messageData); + + // Compile any configuration variables + $helperInstance->getTemplateInstance()->compileConfigInVariables(); + + // Deliver the package + $helperInstance->sendPackage($nodeInstance); + + /* + * Remove temporary configuration + */ + $this->removeMessageConfigurationData($messageData); + + // Debug message + /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('MESSAGE-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Answer message has been prepared.'); + } + + /** + * Prepares the next message + * + * @param $messageData An array with all message data + * @param $packageInstance An instance of a Deliverable instance + * @return void + */ + protected function prepareNextMessage (array $messageData, Deliverable $packageInstance) { + // Debug message + /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('MESSAGE-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Going to send next message ...'); + + // Get a helper instance based on this handler's name + $helperInstance = ObjectFactory::createObjectByConfiguredName('node_next_' . $this->getHandlerName() . '_helper_class', array($messageData)); + + // Get node instance + $nodeInstance = NodeObjectFactory::createNodeInstance(); + + // Load descriptor XML + $helperInstance->loadDescriptorXml($nodeInstance); + + /* + * Set missing (temporary) configuration data, mostly it needs to be + * copied from message data array. + */ + $this->initMessageConfigurationData($messageData); + + // Compile any configuration variables + $helperInstance->getTemplateInstance()->compileConfigInVariables(); + + // Deliver the package + $helperInstance->sendPackage($nodeInstance); + + /* + * Remove temporary configuration + */ + $this->removeMessageConfigurationData($messageData); + + // Debug message + /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('MESSAGE-HANDLER[' . __METHOD__ . ':' . __LINE__ . ']: Next message has been prepared.'); + } + + /** + * Initializes configuration data from given message data array + * + * @param $messageData An array with all message data + * @return void + */ + abstract protected function initMessageConfigurationData (array $messageData); + + /** + * Removes configuration data with given message data array from global + * configuration + * + * @param $messageData An array with all message data + * @return void + */ + abstract protected function removeMessageConfigurationData (array $messageData); + +} diff --git a/application/hub/classes/handler/raw_data/network/.htaccess b/application/hub/classes/handler/raw_data/network/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/application/hub/classes/handler/raw_data/network/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/application/hub/classes/handler/raw_data/network/class_ b/application/hub/classes/handler/raw_data/network/class_ new file mode 100644 index 000000000..011cd0b2d --- /dev/null +++ b/application/hub/classes/handler/raw_data/network/class_ @@ -0,0 +1,69 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Hub 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 ???RawDataHandler extends BaseRawDataHandler implements Networkable { + /** + * Last socket error (default: Success) + */ + private $lastSocketError = 0; + + /** + * Protected constructor + * + * @return void + */ + protected function __construct () { + // Call parent constructor + parent::__construct(__CLASS__); + + // Set handler name + $this->setHandlerName('|||'); + } + + /** + * Creates an instance of this class + * + * @return $handlerInstance An instance of a Networkable class + */ + public static final function create???RawDataHandler () { + // Get new instance + $handlerInstance = new ???RawDataHandler(); + + // Return the prepared instance + return $handlerInstance; + } + + /** + * Processes raw data from given resource. This is mostly useful for TCP + * package handling and is implemented in the ???Listener class + * + * @param $resource A valid socket resource array + * @return void + */ + public function processRawDataFromResource (array $socketArray) { + } + +} diff --git a/application/hub/classes/handler/raw_data/network/class_BaseRawDataHandler.php b/application/hub/classes/handler/raw_data/network/class_BaseRawDataHandler.php new file mode 100644 index 000000000..f06ab8367 --- /dev/null +++ b/application/hub/classes/handler/raw_data/network/class_BaseRawDataHandler.php @@ -0,0 +1,188 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Hub 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 BaseRawDataHandler extends BaseHandler { + // Error codes: + // - Socket raw data stream errors + const SOCKET_ERROR_UNKNOWN = 'unknown_error'; // Unknown error (should not happen) + const SOCKET_ERROR_TRANSPORT_ENDPOINT = 'transport_endpoint'; // Transport endpoint has closed + const SOCKET_ERROR_INVALID_BASE64_MODULO = 'base64_modulo'; // Length is not modulo 4 + const SOCKET_ERROR_INVALID_BASE64_MESSAGE = 'base64_message'; // Raw data is not Base64-encoded + const SOCKET_ERROR_UNHANDLED = 'unhandled_package'; // Unhandled raw data (not bad) + const SOCKET_ERROR_CONNECTION_REFUSED = 'connection_refused'; // The name says it: connection refused + const SOCKET_ERROR_CONNECTION_TIMED_OUT = 'connection_timed_out'; // The name says it: connection attempt has timed-out + const SOCKET_ERROR_OPERATION_IN_PROGRESS = 'operation_in_progress'; // 'Operation now in progress' + const SOCKET_ERROR_OPERATION_ALREADY_PROGRESS = 'operation_already_progress'; // 'Operation already in progress' + const SOCKET_ERROR_RESOURCE_UNAVAILABLE = 'resource_unavailable'; // 'Resource temporary unavailable' + const SOCKET_ERROR_NO_ROUTE_TO_HOST = 'no_route_to_host'; // The name says it: no route to host + const SOCKET_ERROR_CONNECTION_RESET_BY_PEER = 'connection_reset_by_peer'; // Connection reset by peer + const SOCKET_ERROR_BROKEN_PIPE = 'broken_pipe'; // Broken pipe + const SOCKET_ERROR_PERMISSION_DENIED = 'permission_denied'; // Permission denied + const SOCKET_CONNECTED = 'connected'; // Nothing errorous happens, socket is connected + + // - Package errors + const PACKAGE_ERROR_INVALID_DATA = 'invalid_data'; // Invalid data in package found + const PACKAGE_ERROR_INCOMPLETE_DATA = 'incomplete_data'; // Incomplete data sent (e.g. field is missing) + const PACKAGE_ERROR_INVALID_CONTENT = 'invalid_content'; // Content is invalid (e.g. not well-formed) + const PACKAGE_ERROR_RECIPIENT_MISMATCH = 'recipient_error'; // Recipient is not us + const PACKAGE_LEVEL_CHECK_OKAY = 'checked_package'; // Package is fine + + // Package data + const PACKAGE_RAW_DATA = 'raw_data'; + const PACKAGE_ERROR_CODE = 'error_code'; + + // Start/end marker + const STREAM_START_MARKER = '[[S]]'; + const STREAM_END_MARKER = '[[E]]'; + + /** + * Stacker for raw data + */ + const STACKER_NAME_RAW_DATA = 'raw_data'; + + /** + * Error code from socket + */ + private $errorCode = -1; + + /** + * Protected constructor + * + * @param $className Name of the class + * @return void + */ + protected function __construct ($className) { + // Call parent constructor + parent::__construct($className); + + // Set error code to 'unknown' + $this->setErrorCode(self::SOCKET_ERROR_UNKNOWN); + + // Init stacker instance for processed raw data + $stackInstance = ObjectFactory::createObjectByConfiguredName('node_raw_data_stacker_class'); + + // Remember this in this package handler + $this->setStackInstance($stackInstance); + + // Init stacker + $this->initStack(); + } + + /** + * Initializes the stacker for raw data + * + * @return void + */ + protected function initStack () { + $this->getStackInstance()->initStack(self::STACKER_NAME_RAW_DATA); + } + + /** + * Adds given raw data to the raw data stacker + * + * @param $rawData raw data from the socket resource + * @return void + */ + protected function addRawDataToStacker ($rawData) { + /* + * Add the deocoded data and error code to the stacker so other classes + * (e.g. NetworkPackage) can "pop" it from the stacker. + */ + $this->getStackInstance()->pushNamed(self::STACKER_NAME_RAW_DATA, array( + self::PACKAGE_RAW_DATA => $rawData, + self::PACKAGE_ERROR_CODE => $this->getErrorCode() + )); + } + + /** + * Checks whether raw data is pending for further processing. + * + * @return $isPending Whether raw data is pending + */ + public function isRawDataPending () { + // Does the stacker have some entries (not empty)? + $isPending = (!$this->getStackInstance()->isStackEmpty(self::STACKER_NAME_RAW_DATA)); + + // Return it + return $isPending; + } + + /** + * "Getter" for next raw data from the stacker + * + * @return $rawData Raw data from the stacker + */ + public function getNextRawData () { + // "Pop" the raw data from the stacker + $rawData = $this->getStackInstance()->popNamed(self::STACKER_NAME_RAW_DATA); + + // And return it + return $rawData; + } + + /** + * Checks whether the 'recipient' field matches our own an universal node + * locator. + * + * @param $packageData Raw package data + * @return $matches Whether it matches + * @todo This method will be moved to a better place + */ + protected function ifRecipientMatchesOwnUniversalNodeLocator (array $packageData) { + // Construct own address first + $ownAddress = NodeObjectFactory::createNodeInstance()->determineUniversalNodeLocator(); + + // Does it match? + $matches = ($ownAddress === $packageData[NetworkPackage::PACKAGE_DATA_RECIPIENT]); + + // Return result + return $matches; + } + + /** + * Setter for error code + * + * @param $errorCode The error code we shall set + * @return void + */ + public final function setErrorCode ($errorCode) { + $this->errorCode = $errorCode; + } + + /** + * Getter for error code + * + * @return $errorCode The error code + */ + public final function getErrorCode () { + return $this->errorCode; + } + +} diff --git a/application/hub/classes/info/.htaccess b/application/hub/classes/info/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/application/hub/classes/info/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/application/hub/classes/info/class_ b/application/hub/classes/info/class_ new file mode 100644 index 000000000..a7d46d78d --- /dev/null +++ b/application/hub/classes/info/class_ @@ -0,0 +1,55 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Hub Developer Team + * @license GNU GPL 3.0 or any newer version + * @link http://www.ship-simu.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 ???Info extends BaseInfo implements ShareableInfo { + /** + * Protected constructor + * + * @return void + */ + protected function __construct () { + // Call parent constructor + parent::__construct(__CLASS__); + } + + /** + * Creates an instance of this class + * + * @return $infoInstance An instance of a ShareableInfo class + */ + public final static function create???Info () { + // Get new instance + $infoInstance = new ???Info(); + + // Return the prepared instance + return $infoInstance; + } + +} diff --git a/application/hub/classes/info/class_BaseInfo.php b/application/hub/classes/info/class_BaseInfo.php new file mode 100644 index 000000000..837ebb269 --- /dev/null +++ b/application/hub/classes/info/class_BaseInfo.php @@ -0,0 +1,42 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Hub Developer Team + * @license GNU GPL 3.0 or any newer version + * @link http://www.ship-simu.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 BaseInfo extends BaseHubSystem { + /** + * Protected constructor + * + * @param $className Name of the class + * @return void + */ + protected function __construct ($className) { + // Call parent constructor + parent::__construct($className); + } + +} diff --git a/application/hub/classes/info/connection/.htaccess b/application/hub/classes/info/connection/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/application/hub/classes/info/connection/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/application/hub/classes/info/connection/class_ConnectionInfo.php b/application/hub/classes/info/connection/class_ConnectionInfo.php new file mode 100644 index 000000000..49473c824 --- /dev/null +++ b/application/hub/classes/info/connection/class_ConnectionInfo.php @@ -0,0 +1,123 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Hub Developer Team + * @license GNU GPL 3.0 or any newer version + * @link http://www.ship-simu.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 ConnectionInfo extends BaseInfo implements ShareableInfo, Registerable { + /** + * Protected constructor + * + * @return void + */ + protected function __construct () { + // Call parent constructor + parent::__construct(__CLASS__); + } + + /** + * Creates an instance of this class + * + * @return $infoInstance An instance of a ShareableInfo class + */ + public final static function createConnectionInfo () { + // Get new instance + $infoInstance = new ConnectionInfo(); + + // Return the prepared instance + return $infoInstance; + } + + /** + * Fills the information class with data from a Listenable instance + * + * @param $listenerInstance An instance of a Listenable class + * @return void + */ + public function fillWithListenerInformation (Listenable $listenerInstance) { + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: protocolName=' . $listenerInstance->getProtocolName() . ',listenerInstance=' . $listenerInstance->__toString() . ' - CALLED!'); + + // Fill the generic array with several data from the listener: + $this->setProtocolName($listenerInstance->getProtocolName()); + $this->setGenericArrayElement('connection', 'dummy', 'dummy', UniversalNodeLocator::UNL_PART_ADDRESS , $listenerInstance->getListenAddress()); + $this->setGenericArrayElement('connection', 'dummy', 'dummy', UniversalNodeLocator::UNL_PART_PORT , $listenerInstance->getListenPort()); + + // Set listener here + $this->setListenerInstance($listenerInstance); + + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: EXIT!'); + } + + /** + * Fills the information class with data from a ConnectionHelper instance + * + * @param $helperInstance An instance of a ConnectionHelper class + * @return void + */ + public function fillWithConnectionHelperInformation (ConnectionHelper $helperInstance) { + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: protocolName=' . $helperInstance->getProtocolName() . ',helperInstance=' . $helperInstance->__toString() . ',socketResource=' . $helperInstance->getSocketResource() . ' - CALLED!'); + + // Fill the generic array with several data from the listener: + $this->setProtocolName($helperInstance->getProtocolName()); + $this->setGenericArrayElement('connection', 'dummy', 'dummy', UniversalNodeLocator::UNL_PART_ADDRESS , $helperInstance->getAddress()); + $this->setGenericArrayElement('connection', 'dummy', 'dummy', UniversalNodeLocator::UNL_PART_PORT , $helperInstance->getConnectionPort()); + + // Set helper here + $this->setHelperInstance($helperInstance); + + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: EXIT!'); + } + + /** + * Getter for address + * + * @return $address Address from shared information + */ + public final function getAddress () { + // Return it from generic array + return $this->getGenericArrayElement('connection', 'dummy', 'dummy', UniversalNodeLocator::UNL_PART_ADDRESS); + } + + /** + * Getter for port + * + * @return $port Port from shared information + */ + public final function getPort () { + // Return it from generic array + return $this->getGenericArrayElement('connection', 'dummy', 'dummy', UniversalNodeLocator::UNL_PART_PORT); + } + +} diff --git a/application/hub/classes/listener/class_ b/application/hub/classes/listener/class_ new file mode 100644 index 000000000..7c08a81c7 --- /dev/null +++ b/application/hub/classes/listener/class_ @@ -0,0 +1,109 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Hub Developer Team + * @license GNU GPL 3.0 or any newer version + * @link http://www.ship-simu.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 ???Listener extends BaseListener implements Listenable { + /** + * Protected constructor + * + * @return void + */ + protected function __construct () { + // Call parent constructor + parent::__construct(__CLASS__); + + // Set the protocol to !!! + $this->setProtocolName('!!!'); + } + + /** + * Creates an instance of this class + * + * @return $listenerInstance An instance a prepared listener class + */ + public final static function create???Listener () { + // Get new instance + $listenerInstance = new ???Listener(); + + // Return the prepared instance + return $listenerInstance; + } + + /** + * Initializes the listener by setting up the required socket server + * + * @return void + * @todo 0% done + */ + public function initListener() { + $this->partialStub('Need to implement this method.'); + } + + /** + * "Listens" for incoming network packages + * + * @return void + * @todo 0% done + */ + public function doListen() { + $this->partialStub('Need to implement this method.'); + } + + /** + * Checks whether the listener would accept the given package data array + * + * @param $packageData Raw package data + * @return $accepts Whether this listener does accept + */ + public function ifListenerAcceptsPackageData (array $packageData) { + $this->partialStub('Need to implement this method.') { + } + + /** + * Monitors incoming raw data from the handler and transfers it to the + * given receiver instance. + * + * @return void + */ + public function monitorIncomingRawData () { + $this->partialStub('Need to implement this method.') { + } + + /** + * Getter for connection type + * + * @return $connectionType Connection type for this listener + */ + public function getConnectionType () { + $this->partialStub('Need to implement this method.') { + } + +} diff --git a/application/hub/classes/listener/class_BaseListener.php b/application/hub/classes/listener/class_BaseListener.php new file mode 100644 index 000000000..938e07c82 --- /dev/null +++ b/application/hub/classes/listener/class_BaseListener.php @@ -0,0 +1,609 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Hub 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 BaseListener extends BaseFrameworkSystem implements Visitable { + // Exception code constants + const EXCEPTION_SOCKET_ALREADY_REGISTERED = 0xa01; + const EXCEPTION_SOCKET_CREATION_FAILED = 0xa02; + const EXCEPTION_NO_SOCKET_ERROR = 0xa03; + const EXCEPTION_CONNECTION_ALREADY_REGISTERED = 0xa04; + const EXCEPTION_UNEXPECTED_PACKAGE_STATUS = 0xa05; + const EXCEPTION_UNSUPPORTED_PACKAGE_CODE_HANDLER = 0xa06; + const EXCEPTION_FINAL_CHUNK_VERIFICATION = 0xa07; + const EXCEPTION_INVALID_DATA_CHECKSUM = 0xa08; + + /** + * Address (IP mostly) we shall listen on + */ + private $listenAddress = '0.0.0.0'; // This is the default and listens on all interfaces + + /** + * Port we shall listen on (or wait for incoming data) + */ + private $listenPort = 0; // This port MUST be changed by your application + + /** + * Whether we are in blocking or non-blocking mode (default: non-blocking + */ + private $blockingMode = false; + + /** + * A peer pool instance + */ + private $poolInstance = NULL; + + /** + * Protected constructor + * + * @param $className Name of the class + * @return void + */ + protected function __construct ($className) { + // Call parent constructor + parent::__construct($className); + } + + /** + * Checks whether the given socket resource is a server socket + * + * @param $socketResource A valid socket resource + * @return $isServerSocket Whether the socket resource is a server socket + */ + protected function isServerSocketResource ($socketResource) { + // Check it + $isServerSocket = ((is_resource($socketResource)) && (!@socket_getpeername($socketResource, $peerName))); + + // We need to clear the error here if it is a resource + if ($isServerSocket === true) { + // Clear the error + //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('socketResource[]=' . gettype($socketResource)); + socket_clear_error($socketResource); + } // END - if + + // Check peer name, it must be empty + $isServerSocket = (($isServerSocket) && (empty($peerName))); + + // Return result + return $isServerSocket; + } + + /** + * Setter for listen address + * + * @param $listenAddress The address this listener should listen on + * @return void + */ + protected final function setListenAddress ($listenAddress) { + $this->listenAddress = (string) $listenAddress; + } + + /** + * Getter for listen address + * + * @return $listenAddress The address this listener should listen on + */ + public final function getListenAddress () { + return $this->listenAddress; + } + + /** + * Setter for listen port + * + * @param $listenPort The port this listener should listen on + * @return void + */ + protected final function setListenPort ($listenPort) { + $this->listenPort = (int) $listenPort; + } + + /** + * Getter for listen port + * + * @return $listenPort The port this listener should listen on + */ + public final function getListenPort () { + return $this->listenPort; + } + + /** + * "Setter" to set listen address from configuration entry + * + * @param $configEntry The configuration entry holding our listen address + * @return void + */ + public final function setListenAddressByConfiguration ($configEntry) { + $this->setListenAddress($this->getConfigInstance()->getConfigEntry($configEntry)); + } + + /** + * "Setter" to set listen port from configuration entry + * + * @param $configEntry The configuration entry holding our listen port + * @return void + */ + public final function setListenPortByConfiguration ($configEntry) { + $this->setListenPort($this->getConfigInstance()->getConfigEntry($configEntry)); + } + + /** + * Setter for blocking-mode + * + * @param $blockingMode Whether blocking-mode is disabled (default) or enabled + * @return void + */ + protected final function setBlockingMode ($blockingMode) { + $this->blockingMode = (boolean) $blockingMode; + } + + /** + * Checks whether blocking-mode is enabled or disabled + * + * @return $blockingMode Whether blocking mode is disabled or enabled + */ + public final function isBlockingModeEnabled () { + return $this->blockingMode; + } + + /** + * Setter for peer pool instance + * + * @param $poolInstance The peer pool instance we shall set + * @return void + */ + protected final function setPoolInstance (PoolablePeer $poolInstance) { + $this->poolInstance = $poolInstance; + } + + /** + * Getter for peer pool instance + * + * @return $poolInstance The peer pool instance we shall set + */ + public final function getPoolInstance () { + return $this->poolInstance; + } + + /** + * Getter for connection type + * + * @return $connectionType Connection type for this listener + */ + public final function getConnectionType () { + // Wrap the real getter + return $this->getProtocolName(); + } + + /** + * Registeres the given socket resource for "this" listener instance. This + * will be done in a seperate class to allow package writers to use it + * again. + * + * @param $socketResource A valid server socket resource + * @return void + * @throws InvalidServerSocketException If the given resource is no server socket + * @throws SocketAlreadyRegisteredException If the given resource is already registered + */ + protected function registerServerSocketResource ($socketResource) { + // First check if it is valid + if (!$this->isServerSocketResource($socketResource)) { + // No server socket + throw new InvalidServerSocketException(array($this, $socketResource), self::EXCEPTION_INVALID_SOCKET); + } elseif ($this->isServerSocketRegistered($socketResource)) { + // Already registered + throw new SocketAlreadyRegisteredException($this, self::EXCEPTION_SOCKET_ALREADY_REGISTERED); + } + + // Get a socket registry instance (singleton) + $registryInstance = SocketRegistryFactory::createSocketRegistryInstance(); + + // Get a connection info instance + $infoInstance = ConnectionInfoFactory::createConnectionInfoInstance($this->getProtocolName(), 'listener'); + + // Will the info instance with listener data + $infoInstance->fillWithListenerInformation($this); + + // Register the socket + $registryInstance->registerSocket($infoInstance, $socketResource); + + // And set it here + $this->setSocketResource($socketResource); + } + + /** + * Checks whether given socket resource is registered in socket registry + * + * @param $socketResource A valid server socket resource + * @return $isRegistered Whether given server socket is registered + */ + protected function isServerSocketRegistered ($socketResource) { + // Get a socket registry instance (singleton) + $registryInstance = SocketRegistryFactory::createSocketRegistryInstance(); + + // Get a connection info instance + $infoInstance = ConnectionInfoFactory::createConnectionInfoInstance($this->getProtocolName(), 'listener'); + + // Will the info instance with listener data + $infoInstance->fillWithListenerInformation($this); + + // Check it + $isRegistered = $registryInstance->isSocketRegistered($infoInstance, $socketResource); + + // Return result + return $isRegistered; + } + + /** + * Accepts the visitor to process the visit "request" + * + * @param $visitorInstance An instance of a Visitor class + * @return void + */ + public function accept (Visitor $visitorInstance) { + // Debug message + //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(strtoupper($this->getProtocolName()) . '-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: ' . $visitorInstance->__toString() . ' has visited ' . $this->__toString() . ' - CALLED!'); + + // Visit this listener + $visitorInstance->visitListener($this); + + // Visit the pool if set + if ($this->getPoolInstance() instanceof Poolable) { + $this->getPoolInstance()->accept($visitorInstance); + } // END - if + + // Debug message + //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(strtoupper($this->getProtocolName()) . '-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: ' . $visitorInstance->__toString() . ' has visited ' . $this->__toString() . ' - EXIT!'); + } + + /** + * Monitors incoming raw data from the handler and transfers it to the + * given receiver instance. This method should not be called, please call + * the decorator's version instead to separator node/client traffic. + * + * @return void + * @throws UnsupportedOperatorException If this method is called by a mistake + */ + public function monitorIncomingRawData () { + throw new UnsupportedOperationException(array($this, __FUNCTION__), self::EXCEPTION_UNSPPORTED_OPERATION); + } + + /** + * Constructs a callable method name from given socket error code. If the + * method is not found, a generic one is used. + * + * @param $errorCode Error code from socket_last_error() + * @return $handlerName Call-back method name for the error handler + * @throws UnsupportedSocketErrorHandlerException If the error handler is not implemented + */ + protected function getSocketErrorHandlerFromCode ($errorCode) { + // Create a name from translated error code + $handlerName = 'socketError' . self::convertToClassName($this->translateSocketErrorCodeToName($errorCode)) . 'Handler'; + + // Is the call-back method there? + if (!method_exists($this, $handlerName)) { + // Please implement this + throw new UnsupportedSocketErrorHandlerException(array($this, $handlerName, $errorCode), BaseConnectionHelper::EXCEPTION_UNSUPPORTED_ERROR_HANDLER); + } // END - if + + // Return it + return $handlerName; + } + + /** + * Translates socket error codes into our own internal names which can be + * used for call-backs. + * + * @param $errorCode The error code from socket_last_error() to be translated + * @return $errorName The translated name (all lower-case, with underlines) + */ + public function translateSocketErrorCodeToName ($errorCode) { + // Nothing bad happened by default + $errorName = BaseRawDataHandler::SOCKET_CONNECTED; + + // Is the code a number, then we have to change it + switch ($errorCode) { + case 0: // Silently ignored, the socket is connected + break; + + case 11: // "Resource temporary unavailable" + $errorName = BaseRawDataHandler::SOCKET_ERROR_RESOURCE_UNAVAILABLE; + break; + + case 13: // "Permission denied" + $errorName = BaseRawDataHandler::SOCKET_ERROR_PERMISSION_DENIED; + break; + + case 32: // "Broken pipe" + $errorName = BaseRawDataHandler::SOCKET_ERROR_BROKEN_PIPE; + break; + + case 104: // "Connection reset by peer" + $errorName = BaseRawDataHandler::SOCKET_ERROR_CONNECTION_RESET_BY_PEER; + break; + + case 107: // "Transport end-point not connected" + case 134: // On some (?) systems for 'transport end-point not connected' + // @TODO On some systems it is 134, on some 107? + $errorName = BaseRawDataHandler::SOCKET_ERROR_TRANSPORT_ENDPOINT; + break; + + case 110: // "Connection timed out" + $errorName = BaseRawDataHandler::SOCKET_ERROR_CONNECTION_TIMED_OUT; + break; + + case 111: // "Connection refused" + $errorName = BaseRawDataHandler::SOCKET_ERROR_CONNECTION_REFUSED; + break; + + case 113: // "No route to host" + $errorName = BaseRawDataHandler::SOCKET_ERROR_NO_ROUTE_TO_HOST; + break; + + case 114: // "Operation already in progress" + $errorName = BaseRawDataHandler::SOCKET_ERROR_OPERATION_ALREADY_PROGRESS; + break; + + case 115: // "Operation now in progress" + $errorName = BaseRawDataHandler::SOCKET_ERROR_OPERATION_IN_PROGRESS; + break; + + default: // Everything else <> 0 + // Unhandled error code detected, so first debug it because we may want to handle it like the others + self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-HUB[' . __METHOD__ . ':' . __LINE__ . '] UNKNOWN ERROR CODE = ' . $errorCode . ', MESSAGE = ' . socket_strerror($errorCode)); + + // Change it only in this class + $errorName = BaseRawDataHandler::SOCKET_ERROR_UNKNOWN; + break; + } + + // Return translated name + return $errorName; + } + + /** + * Shuts down a given socket resource. This method does only ease calling + * the right visitor. + * + * @param $socketResource A valid socket resource + * @return void + */ + public function shutdownSocket ($socketResource) { + // Debug message + self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('HUB-SYSTEM: Shutting down socket resource ' . $socketResource . ' with state ' . $this->getPrintableState() . ' ...'); + + // Set socket resource + $this->setSocketResource($socketResource); + + // Get a visitor instance + $visitorInstance = ObjectFactory::createObjectByConfiguredName('shutdown_socket_visitor_class'); + + // Debug output + self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('HUB-SYSTEM:' . $this->__toString() . ': visitorInstance=' . $visitorInstance->__toString()); + + // Call the visitor + $this->accept($visitorInstance); + } + + /** + * Half-shuts down a given socket resource. This method does only ease calling + * an other visitor than shutdownSocket() does. + * + * @param $socketResource A valid socket resource + * @return void + */ + public function halfShutdownSocket ($socketResource) { + // Debug message + self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('HUB-SYSTEM: Half-shutting down socket resource ' . $socketResource . ' with state ' . $this->getPrintableState() . ' ...'); + + // Set socket resource + $this->setSocketResource($socketResource); + + // Get a visitor instance + $visitorInstance = ObjectFactory::createObjectByConfiguredName('half_shutdown_socket_visitor_class'); + + // Debug output + self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('HUB-SYSTEM:' . $this->__toString() . ': visitorInstance=' . $visitorInstance->__toString()); + + // Call the visitor + $this->accept($visitorInstance); + } + + // ************************************************************************ + // Socket error handler call-back methods + // ************************************************************************ + + /** + * Handles socket error 'permission denied', but does not clear it for + * later debugging purposes. + * + * @param $socketResource A valid socket resource + * @param $socketData A valid socket data array (0 = IP/file name, 1 = port) + * @return void + * @throws SocketBindingException The socket could not be bind to + */ + protected function socketErrorPermissionDeniedHandler ($socketResource, array $socketData) { + // Get socket error code for verification + $socketError = socket_last_error($socketResource); + + // Get error message + $errorMessage = socket_strerror($socketError); + + // Shutdown this socket + $this->shutdownSocket($socketResource); + + // Throw it again + throw new SocketBindingException(array($this, $socketData, $socketResource, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET); + } + + /** + * "Listens" for incoming network packages + * + * @param $peerSuffix Suffix for peer name (e.g. :0 for TCP(/UDP?) connections) + * @return void + * @throws InvalidSocketException If an invalid socket resource has been found + */ + protected function doListenSocketSelect ($peerSuffix) { + // Check on all instances + assert($this->getPoolInstance() instanceof Poolable); + assert(is_resource($this->getSocketResource())); + + // Get all readers + $readers = $this->getPoolInstance()->getAllSingleSockets(); + $writers = array(); + $excepts = array(); + + // Check if we have some peers left + $left = socket_select( + $readers, + $writers, + $excepts, + 0, + 150 + ); + + // Some new peers found? + if ($left < 1) { + // Debug message + //* EXTREME-NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('TCP-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: left=' . $left . ',serverSocket=' . $this->getSocketResource() . ',readers=' . print_r($readers, true)); + + // Nothing new found + return; + } // END - if + + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('TCP-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: serverSocket=' . $this->getSocketResource() . ',readers=' . print_r($readers, true)); + + // Do we have changed peers? + if (in_array($this->getSocketResource(), $readers)) { + /* + * Then accept it, if this socket is set to non-blocking IO and the + * connection is NOT sending any data, socket_read() may throw + * error 11 (Resource temporary unavailable). This really nasty + * because if you have blocking IO socket_read() will wait and wait + * and wait ... + */ + $newSocket = socket_accept($this->getSocketResource()); + + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('TCP-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: newSocket=' . $newSocket . ',serverSocket=' .$this->getSocketResource()); + + // Array for timeout settings + $options = array( + // Seconds + 'sec' => $this->getConfigInstance()->getConfigEntry('tcp_socket_accept_wait_sec'), + // Milliseconds + 'usec' => $this->getConfigInstance()->getConfigEntry('tcp_socket_accept_wait_usec') + ); + + // Set timeout to configured seconds + // @TODO Does this work on Windozer boxes??? + if (!socket_set_option($newSocket, SOL_SOCKET, SO_RCVTIMEO, $options)) { + // Handle this socket error with a faked recipientData array + $this->handleSocketError(__METHOD__, __LINE__, $newSocket, array('0.0.0.0', '0')); + } // END - if + + // Output result (only for debugging!) + /* + $option = socket_get_option($newSocket, SOL_SOCKET, SO_RCVTIMEO); + self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SO_RCVTIMEO[' . gettype($option) . ']=' . print_r($option, true)); + */ + + // Enable SO_OOBINLINE + if (!socket_set_option($newSocket, SOL_SOCKET, SO_OOBINLINE ,1)) { + // Handle this socket error with a faked recipientData array + $this->handleSocketError(__METHOD__, __LINE__, $newSocket, array('0.0.0.0', '0')); + } // END - if + + // Set non-blocking + if (!socket_set_nonblock($newSocket)) { + // Handle this socket error with a faked recipientData array + $this->handleSocketError(__METHOD__, __LINE__, $newSocket, array('0.0.0.0', '0')); + } // END - if + + // Add it to the peers + $this->getPoolInstance()->addPeer($newSocket, BaseConnectionHelper::CONNECTION_TYPE_INCOMING); + + // Get peer name + if (!socket_getpeername($newSocket, $peerName)) { + // Handle this socket error with a faked recipientData array + $this->handleSocketError(__METHOD__, __LINE__, $newSocket, array('0.0.0.0', '0')); + } // END - if + + // Get node instance + $nodeInstance = NodeObjectFactory::createNodeInstance(); + + // Create a faked package data array + $packageData = array( + NetworkPackage::PACKAGE_DATA_SENDER => $peerName . $peerSuffix, + NetworkPackage::PACKAGE_DATA_RECIPIENT => $nodeInstance->getSessionId(), + NetworkPackage::PACKAGE_DATA_STATUS => NetworkPackage::PACKAGE_STATUS_FAKED + ); + + // Get a connection info instance + $infoInstance = ConnectionInfoFactory::createConnectionInfoInstance($this->getProtocolName(), 'listener'); + + // Will the info instance with listener data + $infoInstance->fillWithListenerInformation($this); + + // Get a socket registry + $registryInstance = SocketRegistryFactory::createSocketRegistryInstance(); + + // Register the socket with the registry and with the faked array + $registryInstance->registerSocket($infoInstance, $newSocket, $packageData); + } // END - if + + // Do we have to rewind? + if (!$this->getIteratorInstance()->valid()) { + // Rewind the list + $this->getIteratorInstance()->rewind(); + } // END - if + + // Get the current value + $currentSocket = $this->getIteratorInstance()->current(); + + // Handle it here, if not main server socket + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('TCP-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: currentSocket=' . $currentSocket[BasePool::SOCKET_ARRAY_RESOURCE] . ',type=' . $currentSocket[BasePool::SOCKET_ARRAY_CONN_TYPE] . ',serverSocket=' . $this->getSocketResource()); + if (($currentSocket[BasePool::SOCKET_ARRAY_CONN_TYPE] != BaseConnectionHelper::CONNECTION_TYPE_SERVER) && ($currentSocket[BasePool::SOCKET_ARRAY_RESOURCE] != $this->getSocketResource())) { + // ... or else it will raise warnings like 'Transport endpoint is not connected' + $this->getHandlerInstance()->processRawDataFromResource($currentSocket); + } // END - if + + // Advance to next entry. This should be the last line. + $this->getIteratorInstance()->next(); + } + +} diff --git a/application/hub/classes/listener/class_BaseListenerDecorator.php b/application/hub/classes/listener/class_BaseListenerDecorator.php new file mode 100644 index 000000000..1cb1d15da --- /dev/null +++ b/application/hub/classes/listener/class_BaseListenerDecorator.php @@ -0,0 +1,159 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Hub 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 BaseListenerDecorator extends BaseDecorator implements Visitable { + /** + * Listener type + */ + private $listenerType = 'invalid'; + + /** + * Protected constructor + * + * @param $className Name of the class + * @return void + */ + protected function __construct ($className) { + // Call parent constructor + parent::__construct($className); + } + + /** + * Getter for listen address + * + * @return $listenAddress The address this listener should listen on + */ + public final function getListenAddress () { + return $this->getListenerInstance()->getListenAddress(); + } + + /** + * Getter for listen port + * + * @return $listenPort The port this listener should listen on + */ + public final function getListenPort () { + return $this->getListenerInstance()->getListenPort(); + } + + /** + * Getter for connection type + * + * @return $connectionType Connection type for this listener + */ + public final function getConnectionType () { + return $this->getListenerInstance()->getConnectionType(); + } + + /** + * Accepts the visitor to process the visit "request" + * + * @param $visitorInstance An instance of a Visitor class + * @return void + */ + public function accept (Visitor $visitorInstance) { + // Visit this decorator + $visitorInstance->visitDecorator($this); + + // Visit the covered class + $visitorInstance->visitListener($this->getListenerInstance()); + } + + /** + * Getter for listener type. + * + * @return $listenerType The listener's type (hub/peer) + */ + public final function getListenerType () { + return $this->listenerType; + } + + /** + * Setter for listener type. + * + * @param $listenerType The listener's type (hub/peer) + * @return void + */ + protected final function setListenerType ($listenerType) { + $this->listenerType = $listenerType; + } + + /** + * Getter for peer pool instance + * + * @return $poolInstance A peer pool instance + */ + public final function getPoolInstance () { + return $this->getListenerInstance()->getPoolInstance(); + } + + /** + * Monitors incoming raw data from the handler and transfers it to the + * given receiver instance. + * + * @return void + */ + public function monitorIncomingRawData () { + // Get the handler instance + $handlerInstance = $this->getListenerInstance()->getHandlerInstance(); + + /* + * Does the deocorated listener (or even a decorator again) have a + * handler assigned? Remember that a handler will hold all incoming raw + * data and not a listener. + */ + if (!$handlerInstance instanceof Networkable) { + // Skip this silently for now. Later on, this will become mandatory! + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('No handler assigned to this listener decorator. this=' . $this->__toString() . ', listenerInstance=' . $this->getListenerInstance()->__toString()); + return; + } // END - if + + // Does the handler have some decoded data pending? + if (!$handlerInstance->isRawDataPending()) { + // No data is pending so skip further code silently + return; + } // END - if + + // Get receiver (network package) instance + $receiverInstance = NetworkPackageFactory::createNetworkPackageInstance(); + + /* + * There is some decoded data waiting. The receiver instance is an + * abstract network package (which can be received and sent out) so + * handle the decoded data over. At this moment it is not needed to + * know if the decoded data origins from a TCP or UDP connection so it + * can just be passed over to the network package receiver. + */ + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-LISTENER-DECORATOR[' . __METHOD__ . ':' . __LINE__ . '] Going to handle over some raw data to receiver instance (' . $receiverInstance->__toString() . ') ...'); + $receiverInstance->addRawDataToIncomingStack($handlerInstance); + } + +} diff --git a/application/hub/classes/listener/socket/.htaccess b/application/hub/classes/listener/socket/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/application/hub/classes/listener/socket/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/application/hub/classes/listener/socket/class_SocketFileListener.php b/application/hub/classes/listener/socket/class_SocketFileListener.php new file mode 100644 index 000000000..4137be69b --- /dev/null +++ b/application/hub/classes/listener/socket/class_SocketFileListener.php @@ -0,0 +1,228 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Hub Developer Team + * @license GNU GPL 3.0 or any newer version + * @link http://www.ship-simu.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 SocketFileListener extends BaseListener implements Listenable { + /** + * Protected constructor + * + * @return void + */ + protected function __construct () { + // Call parent constructor + parent::__construct(__CLASS__); + + // Set the protocol to file + $this->setProtocolName('file'); + } + + /** + * Creates an instance of this class + * + * @return $listenerInstance An instance a prepared listener class + */ + public final static function createSocketFileListener () { + // Get new instance + $listenerInstance = new SocketFileListener(); + + // Return the prepared instance + return $listenerInstance; + } + + /** + * Initializes the listener by setting up the required socket server + * + * @return void + */ + public function initListener() { + // Init socket + $mainSocket = socket_create(AF_UNIX, SOCK_STREAM, 0); + + // Is the socket resource valid? + if (!is_resource($mainSocket)) { + // Something bad happened + throw new InvalidSocketException(array($this, $mainSocket), BaseListener::EXCEPTION_INVALID_SOCKET); + } // END - if + + // Get socket error code for verification + $socketError = socket_last_error($mainSocket); + + // Check if there was an error else + if ($socketError > 0) { + // Handle this socket error with a faked recipientData array + $this->handleSocketError(__METHOD__, __LINE__, $mainSocket, array('null', '0')); + } // END - if + + // Create file name + $socketFile = self::createTempPathForFile($this->getConfigInstance()->getConfigEntry('ipc_socket_file_name')); + + // Debug message + self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-FILE-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: socketFile=' . $socketFile . ' ...'); + + // File name must not be empty + assert(!empty($socketFile)); + + // Is the file there? + if ((FrameworkBootstrap::isReachableFilePath($socketFile)) && (file_exists($socketFile))) { + // Old socket found + self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-FILE-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: WARNING: Old socket at ' . $socketFile . ' found. Will not start.'); + + // Shutdown this socket + $this->shutdownSocket($mainSocket); + + // Quit here + exit; + } // END - if + + // Debug message + self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-FILE-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: Binding to ' . $socketFile . ' ...'); + + // Try to bind to it + if (!socket_bind($mainSocket, $socketFile)) { + // Handle error here + $this->handleSocketError(__METHOD__, __LINE__, $mainSocket, array($socketFile, '0')); + /* + // Get socket error code for verification + $socketError = socket_last_error($mainSocket); + + // Get error message + $errorMessage = socket_strerror($socketError); + + // Shutdown this socket + $this->shutdownSocket($mainSocket); + + // And throw again + throw new InvalidSocketException(array($this, $mainSocket, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET); + */ + } // END - if + + // Start listen for connections + self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-FILE-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: Listening for connections.'); + if (!socket_listen($mainSocket)) { + // Handle this socket error with a faked recipientData array + $this->handleSocketError(__METHOD__, __LINE__, $mainSocket, array($socketFile, '0')); + /* + // Get socket error code for verification + $socketError = socket_last_error($mainSocket); + + // Get error message + $errorMessage = socket_strerror($socketError); + + // Shutdown this socket + $this->shutdownSocket($mainSocket); + + // And throw again + throw new InvalidSocketException(array($this, $mainSocket, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET); + */ + } // END - if + + // Now, we want non-blocking mode + self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-FILE-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: Setting non-blocking mode.'); + if (!socket_set_nonblock($mainSocket)) { + // Handle this socket error with a faked recipientData array + $this->handleSocketError(__METHOD__, __LINE__, $mainSocket, array($socketFile, '0')); + /* + // Get socket error code for verification + $socketError = socket_last_error($mainSocket); + + // Get error message + $errorMessage = socket_strerror($socketError); + + // Shutdown this socket + $this->shutdownSocket($mainSocket); + + // And throw again + throw new InvalidSocketException(array($this, $mainSocket, $socketError, $errorMessage), BaseListener::EXCEPTION_INVALID_SOCKET); + */ + } // END - if + + // Set the main socket + $this->registerServerSocketResource($mainSocket); + + // Initialize the peer pool instance + $poolInstance = ObjectFactory::createObjectByConfiguredName('application_pool_class', array($this)); + + // Add main socket + $poolInstance->addPeer($mainSocket, BaseConnectionHelper::CONNECTION_TYPE_SERVER); + + // And add it to this listener + $this->setPoolInstance($poolInstance); + + // Initialize iterator for listening on packages + $iteratorInstance = ObjectFactory::createObjectByConfiguredName('socket_listen_iterator_class', array($poolInstance->getPoolEntriesInstance())); + + // Rewind it and remember it in this class + $iteratorInstance->rewind(); + $this->setIteratorInstance($iteratorInstance); + + // Initialize the raw data handler + $handlerInstance = ObjectFactory::createObjectByConfiguredName('socket_raw_data_handler_class'); + + // Set it in this class + $this->setHandlerInstance($handlerInstance); + + // Output message + self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-FILE-LISTENER[' . __METHOD__ . ':' . __LINE__ . ']: Socket listener now ready on socket ' . $socketFile . ' for service.'); + } + + /** + * "Listens" for incoming network packages + * + * @return void + */ + public function doListen() { + // Call super method + $this->doListenSocketSelect(''); + } + + /** + * Checks whether the listener would accept the given package data array + * + * @param $packageData Raw package data + * @return $accepts Whether this listener does accept + */ + public function ifListenerAcceptsPackageData (array $packageData) { + $this->partialStub('Need to implement this method.'); + } + + /** + * Monitors incoming raw data from the handler and transfers it to the + * given receiver instance. + * + * @return void + */ + public function monitorIncomingRawData () { + $this->partialStub('Need to implement this method.'); + } + +} diff --git a/application/hub/classes/listener/socket/decorator/.htaccess b/application/hub/classes/listener/socket/decorator/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/application/hub/classes/listener/socket/decorator/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/application/hub/classes/listener/socket/decorator/class_SocketFileListenerDecorator.php b/application/hub/classes/listener/socket/decorator/class_SocketFileListenerDecorator.php new file mode 100644 index 000000000..8084d5599 --- /dev/null +++ b/application/hub/classes/listener/socket/decorator/class_SocketFileListenerDecorator.php @@ -0,0 +1,101 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Hub 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 SocketFileListenerDecorator extends BaseListenerDecorator implements Listenable { + /** + * Protected constructor + * + * @return void + */ + protected function __construct () { + // Call parent constructor + parent::__construct(__CLASS__); + + // Set listener type and protocol name + $this->setListenerType('hub'); + $this->setProtocolName('tcp'); + } + + /** + * Creates an instance of this class + * + * @param $listenerInstance A Listener instance + * @return $decoratorInstance An instance a prepared listener decorator class + */ + public static final function createSocketFileListenerDecorator (Listenable $listenerInstance) { + // Get new instance + $decoratorInstance = new SocketFileListenerDecorator(); + + // Set the application instance + $decoratorInstance->setListenerInstance($listenerInstance); + + // Return the prepared instance + return $decoratorInstance; + } + + /** + * Initializes the listener by setting up the required socket server + * + * @return void + */ + public function initListener () { + $this->partialStub('WARNING: This method should not be called.'); + } + + /** + * "Listens" for incoming network packages + * + * @return void + */ + public function doListen () { + // Handle generic TCP package + $this->getListenerInstance()->doListen(); + + // Handle hub TCP package + $this->partialStub('Need to handle hub TCP package.'); + } + + /** + * Checks whether the listener would accept the given package data array + * + * @param $packageData Raw package data + * @return $accepts Whether this listener does accept + */ + public function ifListenerAcceptsPackageData (array $packageData) { + // Get a tags instance + $tagsInstance = PackageTagsFactory::createPackageTagsInstance(); + + // So is the package accepted with this listener? + $accepts = $tagsInstance->ifPackageDataIsAcceptedByListener($packageData, $this); + + // Return the result + return $accepts; + } + +} diff --git a/application/hub/classes/package/class_NetworkPackage.php b/application/hub/classes/package/class_NetworkPackage.php index 27bef5da9..4437bf296 100644 --- a/application/hub/classes/package/class_NetworkPackage.php +++ b/application/hub/classes/package/class_NetworkPackage.php @@ -10,6 +10,7 @@ use Hub\Generic\BaseHubSystem; use Hub\Helper\Connection\BaseConnectionHelper; use Hub\Helper\Connection\ConnectionHelper; use Hub\Helper\HubHelper; +use Hub\Information\ShareableInfo; use Hub\Network\Receive\Receivable; use Hub\Tools\HubTools; @@ -17,7 +18,6 @@ use Hub\Tools\HubTools; use CoreFramework\Factory\Connection\ConnectionInfoFactory; use CoreFramework\Factory\ObjectFactory; use CoreFramework\Factory\Registry\Socket\SocketRegistryFactory; -use CoreFramework\Information\ShareableInfo; use CoreFramework\Registry\Registry; use CoreFramework\Registry\Registerable; use CoreFramework\Visitor\Visitable; diff --git a/application/hub/classes/pools/class_ b/application/hub/classes/pools/class_ index 1499cf185..5b77ee0d8 100644 --- a/application/hub/classes/pools/class_ +++ b/application/hub/classes/pools/class_ @@ -4,10 +4,10 @@ namespace Hub\Pool\; // Import application-specific stuff use Hub\Pool\BasePool; +use Hub\Pool\Poolable; // Import framework stuff use CoreFramework\Listener\Listenable; -use CoreFramework\Pool\Poolable; /** * diff --git a/application/hub/classes/pools/listener/class_DefaultListenerPool.php b/application/hub/classes/pools/listener/class_DefaultListenerPool.php index 954bfe049..5772d3983 100644 --- a/application/hub/classes/pools/listener/class_DefaultListenerPool.php +++ b/application/hub/classes/pools/listener/class_DefaultListenerPool.php @@ -5,11 +5,11 @@ namespace Hub\Pool\Listener; // Import application-specific stuff use Hub\Helper\Node\NodeHelper; use Hub\Pool\BasePool; +use Hub\Pool\Poolable; // Import framework stuff use CoreFramework\Factory\ObjectFactory; use CoreFramework\Listener\Listenable; -use CoreFramework\Pool\Poolable; /** * A default listener pool diff --git a/application/hub/classes/pools/peer/class_DefaultPeerPool.php b/application/hub/classes/pools/peer/class_DefaultPeerPool.php index 46a187a5f..2239f6aef 100644 --- a/application/hub/classes/pools/peer/class_DefaultPeerPool.php +++ b/application/hub/classes/pools/peer/class_DefaultPeerPool.php @@ -4,10 +4,10 @@ namespace Hub\Pool\Peer; // Import application-specific stuff use Hub\Pool\BasePool; +use Hub\Pool\Poolable; // Import framework stuff use CoreFramework\Listener\Listenable; -use CoreFramework\Pool\Poolable; /** * A default peer pool class diff --git a/application/hub/classes/registry/socket/.htaccess b/application/hub/classes/registry/socket/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/application/hub/classes/registry/socket/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/application/hub/classes/registry/socket/class_SocketRegistry.php b/application/hub/classes/registry/socket/class_SocketRegistry.php new file mode 100644 index 000000000..b60fb2d9e --- /dev/null +++ b/application/hub/classes/registry/socket/class_SocketRegistry.php @@ -0,0 +1,349 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Hub 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 SocketRegistry extends BaseRegistry implements Register, RegisterableSocket { + // Exception constants + const SOCKET_NOT_REGISTERED = 0xd200; + + /** + * Instance of this class + */ + private static $registryInstance = NULL; + + /** + * Protected constructor + * + * @return void + */ + protected function __construct () { + // Call parent constructor + parent::__construct(__CLASS__); + } + + /** + * Creates a singleton instance of this registry class + * + * @return $registryInstance An instance of this class + */ + public static final function createSocketRegistry () { + // Is an instance there? + if (is_null(self::$registryInstance)) { + // Not yet, so create one + self::$registryInstance = new SocketRegistry(); + } // END - if + + // Return the instance + return self::$registryInstance; + } + + /** + * "Getter" to get a string respresentation for a key for the sub-registry + * in this format: class:type:port + * + * @param $infoInstance An instance of a ShareableInfo class + * @return $key A string representation of the socket for the registry + */ + private function getSubRegistryKey (ShareableInfo $infoInstance) { + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-REGISTRY[' . __METHOD__ . ':' . __LINE__ . ']: infoInstance=' . $infoInstance->__toString() . ' - CALLED!'); + + // Get address and port + $address = $infoInstance->getAddress(); + $port = $infoInstance->getPort(); + + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-REGISTRY[' . __METHOD__ . ':' . __LINE__ . ']: address=' . $address . ',port=' . $port); + + // Get connection type and port number and add both together + $key = sprintf('%s:%s:%s:%s', + $infoInstance->__toString(), + $infoInstance->getProtocolName(), + $address, + $port + ); + + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-REGISTRY[' . __METHOD__ . ':' . __LINE__ . ']: key=' . $key . ' - EXIT!'); + + // Return resulting key + return $key; + } + + /** + * "Getter" to get a string respresentation of the listener + * + * @param $infoInstance An instance of a ShareableInfo class + * @return $key A string representation of the listener for the registry + */ + private function getRegistryKeyFromInfo (ShareableInfo $infoInstance) { + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-REGISTRY[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',infoInstance=' . $infoInstance->__toString() . ' - CALLED!'); + + // Get the key + $key = $infoInstance->getProtocolName(); + + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-REGISTRY[' . __METHOD__ . ':' . __LINE__ . ']: key=' . $key . ' - EXIT!'); + + // Return resulting key + return $key; + } + + /** + * Checks whether the shared connection info is registered + * + * @param $infoInstance An instance of a ShareableInfo class + * @return $isRegistered Whether the listener is registered + */ + private function isInfoRegistered (ShareableInfo $infoInstance) { + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-REGISTRY[' . __METHOD__ . ':' . __LINE__ . ']:info=' . $infoInstance->getProtocolName() . ' - CALLED!'); + + // Get the key + $key = $this->getRegistryKeyFromInfo($infoInstance); + + // Determine it + $isRegistered = $this->instanceExists($key); + + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-REGISTRY[' . __METHOD__ . ':' . __LINE__ . ']:info=' . $infoInstance->getProtocolName() . ',isRegistered=' . intval($isRegistered) . ' - EXIT!'); + + // Return result + return $isRegistered; + } + + /** + * Checks whether given socket resource is registered. If $socketResource is + * FALSE only the instance will be checked. + * + * @param $infoInstance An instance of a ShareableInfo class + * @param $socketResource A valid socket resource + * @return $isRegistered Whether the given socket resource is registered + */ + public function isSocketRegistered (ShareableInfo $infoInstance, $socketResource) { + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-REGISTRY[' . __METHOD__ . ':' . __LINE__ . ']:info=' . $infoInstance->getProtocolName() . ',socketResource[' . gettype($socketResource) . ']=' . $socketResource . ' - CALLED!'); + + // Default is not registered + $isRegistered = FALSE; + + // First, check for the instance, there can be only once + if ($this->isInfoRegistered($infoInstance)) { + // That one is found so "get" a registry key from it + $key = $this->getRegistryKeyFromInfo($infoInstance); + + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-REGISTRY[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',info=' . $infoInstance->getProtocolName() . ',socketResource[' . gettype($socketResource) . ']=' . $socketResource . ',key=' . $key . ' - Trying to get instance ...'); + + // Get the registry + $registryInstance = $this->getInstance($key); + + // "Get" a key for the socket + $socketKey = $this->getSubRegistryKey($infoInstance); + + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-REGISTRY[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',info=' . $infoInstance->getProtocolName() . ',socketResource[' . gettype($socketResource) . ']=' . $socketResource . ',key=' . $key . ',socketKey=' . $socketKey . ' - Checking existence ...'); + + // Is it there? + if ($registryInstance->instanceExists($socketKey)) { + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-REGISTRY[' . __METHOD__ . ':' . __LINE__ . ']: Found instance for socketKey=' . $socketKey . ':' . $registryInstance->getInstance($socketKey)); + + // Get the instance + $registeredInstance = $registryInstance->getInstance($socketKey); + + // Is it SocketContainer and same socket? + $isRegistered = (($registeredInstance instanceof SocketContainer) && ($registeredInstance->ifSocketResourceMatches($socketResource))); + + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-REGISTRY[' . __METHOD__ . ':' . __LINE__ . ']: Final result: isRegistered(' . $socketResource . ')=' . intval($isRegistered)); + } // END - if + } // END - if + + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-REGISTRY[' . __METHOD__ . ':' . __LINE__ . ']:info=' . $infoInstance->getProtocolName() . ',socketResource[' . gettype($socketResource) . ']=' . $socketResource . ',isRegistered=' . intval($isRegistered) . ' - EXIT!'); + + // Return the result + return $isRegistered; + } + + /** + * Registeres given socket for listener or throws an exception if it is already registered + * + * @param $infoInstance An instance of a ShareableInfo class + * @param $socketResource A valid socket resource + * @param $packageData Optional raw package data + * @throws SocketAlreadyRegisteredException If the given socket is already registered + * @return void + */ + public function registerSocket (ShareableInfo $infoInstance, $socketResource, array $packageData = array()) { + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-REGISTRY[' . __METHOD__ . ':' . __LINE__ . ']:info=' . $infoInstance->getProtocolName() . ',socketResource[' . gettype($socketResource) . ']=' . $socketResource . ' - CALLED!'); + + // Is the socket already registered? + if ($this->isSocketRegistered($infoInstance, $socketResource)) { + // Throw the exception + throw new SocketAlreadyRegisteredException(array($infoInstance, $socketResource), BaseListener::EXCEPTION_SOCKET_ALREADY_REGISTERED); + } // END - if + + // Does the instance exist? + if (!$this->isInfoRegistered($infoInstance)) { + // No, not found so we create a sub registry (not needed to configure!) + $registryInstance = SubRegistry::createSubRegistry(); + + // Now we can create the sub-registry for this info + $this->addInstance($this->getRegistryKeyFromInfo($infoInstance), $registryInstance); + } else { + // Get the sub-registry back + $registryInstance = $this->getInstance($this->getRegistryKeyFromInfo($infoInstance)); + } + + // Get a key for sub-registries + $socketKey = $this->getSubRegistryKey($infoInstance); + + // Get a socket container + $socketInstance = ObjectFactory::CreateObjectByConfiguredName('socket_container_class', array($socketResource, $infoInstance, $packageData)); + + // We have a sub-registry, the socket key and the socket, now we need to put all together + //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-REGISTRY[' . __METHOD__ . ':' . __LINE__ . ']: socketKey=' . $socketKey . ',socketResource[' . gettype($socketResource) . ']=' . $socketResource . ' - adding socket container instance ...'); + $registryInstance->addInstance($socketKey, $socketInstance); + } + + /** + * Getter for given listener's socket resource + * + * @param $listenerInstance An instance of a Listenable class + * @return $socketResource A valid socket resource + * @throws NoSocketRegisteredException If the requested socket is not registered + */ + public function getRegisteredSocketResource (Listenable $listenerInstance) { + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-REGISTRY[' . __METHOD__ . ':' . __LINE__ . ']:listener=' . $listenerInstance->getConnectionType() . ' - CALLED!'); + + // The socket must be registered before we can return it + if (!$this->isInfoRegistered($listenerInstance)) { + // Throw the exception + throw new NoSocketRegisteredException ($listenerInstance, self::SOCKET_NOT_REGISTERED); + } // END - if + + // Now get the key from the listener + $key = $this->getRegistryKeyFromInfo($listenerInstance); + + // And get the registry + $registryInstance = $this->getInstance($key); + + // Get a socket key + $socketKey = $this->getSubRegistryKey($listenerInstance); + + // And the final socket resource + $socketResource = $registryInstance->getInstance($socketKey)->getSocketResource(); + + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-REGISTRY[' . __METHOD__ . ':' . __LINE__ . ']:listener=' . $listenerInstance->getConnectionType() . ',socketResource[' . gettype($socketResource) . ']=' . $socketResource . ' - EXIT!'); + + // Return the resource + return $socketResource; + } + + /** + * "Getter" for info instance from given package data + * + * @param $packageData Raw package data + * @return $infoInstance An instance of a ShareableInfo class + */ + public function getInfoInstanceFromPackageData (array $packageData) { + // Init info instance + $infoInstance = NULL; + //* DEBUG-DIE: */ die(__METHOD__ . ':packageData=' . print_r($packageData, TRUE)); + + // Get all keys and check them + foreach ($this->getInstanceRegistry() as $key => $registryInstance) { + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-REGISTRY[' . __METHOD__ . ':' . __LINE__ . ']: key=' . $key . ',registryInstance=' . $registryInstance->__toString()); + + // This is always a SubRegistry instance + foreach ($registryInstance->getInstanceRegistry() as $subKey => $containerInstance) { + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-REGISTRY[' . __METHOD__ . ':' . __LINE__ . ']: key=' . $key . ',subKey=' . $subKey . ',containerInstance=' . $containerInstance->__toString()); + + // Is this a SocketContainer instance and is the address the same? + if (($containerInstance instanceof SocketContainer) && ($containerInstance->ifAddressMatches($packageData[NetworkPackage::PACKAGE_DATA_RECIPIENT]))) { + // Debug die + //* DEBUG-DIE: */ die(__METHOD__ . ': containerInstance=' . print_r($containerInstance, TRUE)); + + // Get listener and helper instances + $listenerInstance = $containerInstance->getListenerInstance(); + $helperInstance = $containerInstance->getHelperInstance(); + + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-REGISTRY[' . __METHOD__ . ':' . __LINE__ . ']: key=' . $key . ',subKey=' . $subKey . ',listenerInstance[]=' . gettype($listenerInstance) . ',helperInstance[]=' . gettype($helperInstance)); + + // Is a listener or helper set? + if ($listenerInstance instanceof Listenable) { + // Found a listener, so get the info instance first + $infoInstance = ConnectionInfoFactory::createConnectionInfoInstance($listenerInstance->getProtocolName(), 'helper'); + + // Fill info instance with listener data + $infoInstance->fillWithListenerInformation($listenerInstance); + } elseif ($helperInstance instanceof ConnectionHelper) { + // Found a helper, so get the info instance first + $infoInstance = ConnectionInfoFactory::createConnectionInfoInstance($helperInstance->getProtocolName(), 'helper'); + + // Helper is found + $infoInstance->fillWithConnectionHelperInformation($helperInstance); + } else { + // Not supported state! + $this->debugInstance('[' . __METHOD__ . ':' . __LINE__ . ']: Invalid state found, please report this to the developers with full debug infos.' . PHP_EOL); + } + + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('SOCKET-REGISTRY[' . __METHOD__ . ':' . __LINE__ . ']: key=' . $key . ',subKey=' . $subKey . ',infoInstance[' . gettype($infoInstance) . ']=' . $infoInstance->__toString() . ' with protocol ' . $infoInstance->getProtocolName() . ' - FOUND!'); + break; + } // END - if + } // END - foreach + + // Is no longer NULL set? + if (!is_null($infoInstance)) { + // Then skip here, too + break; + } // END - if + } // END - foreach + + // Return the info instance + return $infoInstance; + } + +} diff --git a/application/hub/classes/tags/package/class_PackageTags.php b/application/hub/classes/tags/package/class_PackageTags.php index 88e4de388..0fe8ad47b 100644 --- a/application/hub/classes/tags/package/class_PackageTags.php +++ b/application/hub/classes/tags/package/class_PackageTags.php @@ -2,6 +2,8 @@ // Own namespace namespace Hub\Tag\Network; +// Import application-specific stuff + // Import framework stuff use CoreFramework\Listener\Listenable; use CoreFramework\Registry\Registry; diff --git a/application/hub/classes/visitor/pool/class_ b/application/hub/classes/visitor/pool/class_ index ef8fafeb4..302c9cf83 100644 --- a/application/hub/classes/visitor/pool/class_ +++ b/application/hub/classes/visitor/pool/class_ @@ -2,8 +2,10 @@ // Own namespace namespace Hub\Visitor\; +// Import application-specific stuff +use Hub\Pool\Poolable; + // Import framework stuff -use CoreFramework\Pool\Poolable; use CoreFramework\Visitor\BaseVisitor; use CoreFramework\Visitor\Visitor; diff --git a/application/hub/classes/visitor/pool/handler/class_Handler b/application/hub/classes/visitor/pool/handler/class_Handler index 2d90b1b6b..6a6d4d180 100644 --- a/application/hub/classes/visitor/pool/handler/class_Handler +++ b/application/hub/classes/visitor/pool/handler/class_Handler @@ -2,8 +2,10 @@ // Own namespace namespace Hub\Visitor\Handler\; +// Import application-specific stuff +use Hub\Pool\Poolable; + // Import framework stuff -use CoreFramework\Pool\Poolable; use CoreFramework\Visitor\BaseVisitor; /** diff --git a/application/hub/classes/visitor/pool/monitor/class_RawDataPoolMonitorVisitor.php b/application/hub/classes/visitor/pool/monitor/class_RawDataPoolMonitorVisitor.php index 0847c642b..a3db3b3b9 100644 --- a/application/hub/classes/visitor/pool/monitor/class_RawDataPoolMonitorVisitor.php +++ b/application/hub/classes/visitor/pool/monitor/class_RawDataPoolMonitorVisitor.php @@ -4,10 +4,10 @@ namespace Hub\Visitor\Monitor\Pool; // Import application-specific stuff use Hub\Network\Receive\Receivable; +use Hub\Pool\Poolable; // Import framework stuff use CoreFramework\Listener\Listenable; -use CoreFramework\Pool\Poolable; use CoreFramework\Visitor\BaseVisitor; /** diff --git a/application/hub/classes/visitor/pool/shutdown/class_Shutdown b/application/hub/classes/visitor/pool/shutdown/class_Shutdown index fdacbb1b0..686b38679 100644 --- a/application/hub/classes/visitor/pool/shutdown/class_Shutdown +++ b/application/hub/classes/visitor/pool/shutdown/class_Shutdown @@ -2,8 +2,10 @@ // Own namespace namespace Hub\Visitor\; +// Import application-specific stuff +use Hub\Pool\Poolable; + // Import framework stuff -use CoreFramework\Pool\Poolable; use CoreFramework\Visitor\BaseVisitor; /** diff --git a/application/hub/classes/visitor/pool/shutdown/class_ShutdownListenerPoolVisitor.php b/application/hub/classes/visitor/pool/shutdown/class_ShutdownListenerPoolVisitor.php index 3e856a474..bd76fe816 100644 --- a/application/hub/classes/visitor/pool/shutdown/class_ShutdownListenerPoolVisitor.php +++ b/application/hub/classes/visitor/pool/shutdown/class_ShutdownListenerPoolVisitor.php @@ -2,9 +2,11 @@ // Own namespace namespace Hub\Visitor\Pool\Listener; +// Import application-specific stuff +use Hub\Pool\Poolable; + // Import framework stuff use CoreFramework\Listener\Listenable; -use CoreFramework\Pool\Poolable; use CoreFramework\Visitor\BaseVisitor; /** diff --git a/application/hub/classes/visitor/tasks/.htaccess b/application/hub/classes/visitor/tasks/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/application/hub/classes/visitor/tasks/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/application/hub/classes/visitor/tasks/class_ActiveTaskVisitor.php b/application/hub/classes/visitor/tasks/class_ActiveTaskVisitor.php new file mode 100644 index 000000000..d2c1989c0 --- /dev/null +++ b/application/hub/classes/visitor/tasks/class_ActiveTaskVisitor.php @@ -0,0 +1,118 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Hub 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 ActiveTaskVisitor extends BaseVisitor implements TaskVisitor, PoolVisitor, ListenerVisitor, DecoratorVisitor { + /** + * Protected constructor + * + * @return void + */ + protected function __construct () { + // Call parent constructor + parent::__construct(__CLASS__); + + // Set visitor mode + $this->setVisitorMode('task'); + } + + /** + * Creates an instance of this class + * + * @return $visitorInstance An instance a Visitorable class + */ + public static final function createActiveTaskVisitor () { + // Get new instance + $visitorInstance = new ActiveTaskVisitor(); + + // Return the prepared instance + return $visitorInstance; + } + + /** + * Visits the given task instance + * + * @param $taskInstance A Taskable instance + * @return void + */ + public function visitTask (Taskable $taskInstance) { + // Execute the task from this visitor + //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ' ]: Visiting task ' . $taskInstance->__toString() . ' - CALLED!'); + $taskInstance->executeTask(); + //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ' ]: Visiting task ' . $taskInstance->__toString() . ' - EXIT!'); + } + + /** + * Pool visitor method for active tasks + * + * @param $poolInstance A Poolable instance + * @return void + */ + public function visitPool (Poolable $poolInstance) { + /** + * We don't need to visit a pool as an active task because a pool can + * never become a task. Instead e.g. by a listener pool we should visit + * all listeners one by one + */ + } + + /** + * Visits the given listener instance + * + * @param $listenerInstance A Listenable instance + * @return void + */ + public function visitListener (Listenable $listenerInstance) { + // Do "listen" here + //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ' ]: Visiting ' . $listenerInstance->__toString() . ' - CALLED!'); + $listenerInstance->doListen(); + //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ' ]: Visiting ' . $listenerInstance->__toString() . ' - FINISH'); + } + + /** + * Visits the given decorator instance + * + * @param $decoratorInstance A decorator instance + * @return void + */ + public function visitDecorator (BaseDecorator $decoratorInstance) { + // A decorator itself can never become an active task so this method + // remains empty. + } + +} diff --git a/application/hub/classes/visitor/tasks/class_ShutdownTaskVisitor.php b/application/hub/classes/visitor/tasks/class_ShutdownTaskVisitor.php new file mode 100644 index 000000000..6b9b0621f --- /dev/null +++ b/application/hub/classes/visitor/tasks/class_ShutdownTaskVisitor.php @@ -0,0 +1,111 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Hub 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 ShutdownTaskVisitor extends BaseVisitor implements TaskVisitor, PoolVisitor, ListenerVisitor, DecoratorVisitor { + /** + * Protected constructor + * + * @return void + */ + protected function __construct () { + // Call parent constructor + parent::__construct(__CLASS__); + + // Set visitor mode + $this->setVisitorMode('task'); + } + + /** + * Creates an instance of this class + * + * @return $visitorInstance An instance a Visitorable class + */ + public static final function createShutdownTaskVisitor () { + // Get new instance + $visitorInstance = new ShutdownTaskVisitor(); + + // Return the prepared instance + return $visitorInstance; + } + + /** + * Visits the given task instance + * + * @param $taskInstance A Taskable instance + * @return void + */ + public function visitTask (Taskable $taskInstance) { + // Shutdown the task instance + $taskInstance->doShutdown(); + } + + /** + * Pool visitor method for active tasks + * + * @param $poolInstance A Poolable instance + * @return void + */ + public function visitPool (Poolable $poolInstance) { + // Shutdown the pool instance + $poolInstance->doShutdown(); + } + + /** + * Visits the given listener instance + * + * @param $listenerInstance A Listenable instance + * @return void + */ + public function visitListener (Listenable $listenerInstance) { + // Shutdown the listener instance + $listenerInstance->doShutdown(); + } + + /** + * Visits the given decorator instance + * + * @param $decoratorInstance A decorator instance + * @return void + */ + public function visitDecorator (BaseDecorator $decoratorInstance) { + // Shutdown the decorator instance + $decoratorInstance->doShutdown(); + } + +} diff --git a/application/hub/config.php b/application/hub/config.php index f538fd9c3..987919206 100644 --- a/application/hub/config.php +++ b/application/hub/config.php @@ -76,10 +76,10 @@ $cfg->setConfigEntry('tcp_listener_class', 'TcpListener'); $cfg->setConfigEntry('udp_listener_class', 'UdpListener'); // CFG: SOCKET-FILE-LISTENER-CLASS -$cfg->setConfigEntry('socket_file_listener_class', 'CoreFramework\Listener\Socket\SocketFileListener'); +$cfg->setConfigEntry('socket_file_listener_class', 'Hub\Listener\Socket\SocketFileListener'); // CFG: SOCKET-FILE-DECORATOR-LISTENER-CLASS -$cfg->setConfigEntry('socket_file_decorator_listener_class', 'CoreFramework\Listener\Socket\SocketFileListenerDecorator'); +$cfg->setConfigEntry('socket_file_decorator_listener_class', 'Hub\Listener\Socket\SocketFileListenerDecorator'); // CFG: NODE-TCP-LISTENER-CLASS $cfg->setConfigEntry('node_tcp_listener_class', 'HubTcpListenerDecorator'); @@ -706,7 +706,7 @@ $cfg->setConfigEntry('deco_package_compressor_class', 'NetworkPackageCompressorD $cfg->setConfigEntry('raw_package_compressor_class', 'ZlibCompressor'); // CFG: SOCKET-REGISTRY-CLASS -$cfg->setConfigEntry('socket_registry_class', 'CoreFramework\Registry\Socket\SocketRegistry'); +$cfg->setConfigEntry('socket_registry_class', 'Hub\Registry\Socket\SocketRegistry'); // CFG: SOCKET-CONTAINER-CLASS $cfg->setConfigEntry('socket_container_class', 'SocketContainer'); @@ -834,7 +834,7 @@ $cfg->setConfigEntry('node_dht_class', 'NodeDhtFacade'); $cfg->setConfigEntry('max_dht_recipients', 10); // CFG: CONNECTION-INFO-CLASS -$cfg->setConfigEntry('connection_info_class', 'CoreFramework\Information\Connection\ConnectionInfo'); +$cfg->setConfigEntry('connection_info_class', 'Hub\Information\Connection\ConnectionInfo'); // CFG: DEFAULT-HUB-CONSOLE-CONTROLLER $cfg->setConfigEntry('default_hub_console_controller', 'main'); diff --git a/application/hub/exceptions.php b/application/hub/exceptions.php index d027c51e2..0dcb971b4 100644 --- a/application/hub/exceptions.php +++ b/application/hub/exceptions.php @@ -8,7 +8,7 @@ use CoreFramework\Object\BaseFrameworkSystem; * * @author Roland Haeder * @version 0.0.0 - * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Core Developer Team + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Hub Developer Team * @license GNU GPL 3.0 or any newer version * * This program is free software: you can redistribute it and/or modify diff --git a/application/hub/exceptions/socket/class_SocketShutdownException.php b/application/hub/exceptions/socket/class_SocketShutdownException.php new file mode 100644 index 000000000..33d7ecae2 --- /dev/null +++ b/application/hub/exceptions/socket/class_SocketShutdownException.php @@ -0,0 +1,52 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Hub 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 SocketShutdownException extends AbstractSocketException { + /** + * A Constructor for this exception + * + * @param $helperInstance An instance of a ConnectionHelper class + * @param $code Error code + * @return void + */ + public function __construct (ConnectionHelper $helperInstance, $code) { + // Get socket resource + $socketResource = $helperInstance->getSocketResource(); + + // Construct the message + $message = sprintf('[%s:] Socket %s cannot be shutdown down. errNo=%s, errStr=%s', + $helperInstance->__toString(), + $socketResource, + socket_last_error($socketResource), + socket_strerror(socket_last_error($socketResource)) + ); + + // Call parent exception constructor + parent::__construct($message, $code); + } + +} diff --git a/application/hub/interfaces/pool/.htaccess b/application/hub/interfaces/pool/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/application/hub/interfaces/pool/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/application/hub/interfaces/pool/class_Poolable.php b/application/hub/interfaces/pool/class_Poolable.php new file mode 100644 index 000000000..e78a4abac --- /dev/null +++ b/application/hub/interfaces/pool/class_Poolable.php @@ -0,0 +1,48 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Hub Developer Team + * @license GNU GPL 3.0 or any newer version + * @link http://www.ship-simu.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 . + */ +interface Poolable extends FrameworkInterface { + + /** + * Pre-shuts down the pool + * + * @return void + */ + function preShutdown (); + + /** + * Adds a listener instance to this pool + * + * @param $poolInstance An instance of a Listenable class + * @return void + */ + function addListener (Listenable $poolInstance); + +} diff --git a/application/hub/interfaces/registry/.htaccess b/application/hub/interfaces/registry/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/application/hub/interfaces/registry/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/application/hub/interfaces/registry/socket/.htaccess b/application/hub/interfaces/registry/socket/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/application/hub/interfaces/registry/socket/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/application/hub/interfaces/registry/socket/class_RegisterableSocket.php b/application/hub/interfaces/registry/socket/class_RegisterableSocket.php new file mode 100644 index 000000000..43be4ece7 --- /dev/null +++ b/application/hub/interfaces/registry/socket/class_RegisterableSocket.php @@ -0,0 +1,72 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Hub 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 . + */ +interface RegisterableSocket extends Registerable { + /** + * Checks whether given socket resource is registered. If $socketResource is + * false only the instance will be checked. + * + * @param $infoInstance An instance of a ShareableInfo class + * @param $socketResource A valid socket resource + * @return $isRegistered Whether the given socket resource is registered + */ + function isSocketRegistered (ShareableInfo $infoInstance, $socketResource); + + /** + * Registeres given socket for listener or throws an exception if it is already registered + * + * @param $infoInstance An instance of a ShareableInfo class + * @param $socketResource A valid socket resource + * @return void + * @throws SocketAlreadyRegisteredException If the given socket is already registered + */ + function registerSocket (ShareableInfo $infoInstance, $socketResource); + + /** + * Getter for given listener's socket resource + * + * @param $listenerInstance An instance of a Listenable class + * @return $socketResource A valid socket resource + * @throws NoSocketRegisteredException If the requested socket is not registered + */ + function getRegisteredSocketResource (Listenable $listenerInstance); + + /** + * "Getter" for info instance from given package data + * + * @param $packageData Raw package data + * @return $infoInstance An instance of a ShareableInfo class + */ + function getInfoInstanceFromPackageData (array $packageData); + +} diff --git a/application/hub/interfaces/resolver/state/.htaccess b/application/hub/interfaces/resolver/state/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/application/hub/interfaces/resolver/state/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/application/hub/interfaces/resolver/state/class_StateResolver.php b/application/hub/interfaces/resolver/state/class_StateResolver.php new file mode 100644 index 000000000..ea4236d19 --- /dev/null +++ b/application/hub/interfaces/resolver/state/class_StateResolver.php @@ -0,0 +1,50 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Hub 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 . + */ +interface StateResolver extends Resolver { + /** + * Returns an state instance for a given package raw data and socket resource + * + * @param $helperInstance An instance of a ConnectionHelper class + * @param $packageData Raw package data + * @param $socketResource A valid socket resource + * @return $stateInstance An instance of the resolved state + */ + static function resolveStateByPackage (ConnectionHelper $helperInstance, array $packageData, $socketResource); + + /** + * Checks whether the given state is valid + * + * @param $stateName The default state we shall execute + * @return $isValid Whether the given state is valid + * @throws EmptyVariableException Thrown if given state is not set + */ + function isStateValid ($stateName); + +} diff --git a/application/hub/interfaces/shareable/.htaccess b/application/hub/interfaces/shareable/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/application/hub/interfaces/shareable/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/application/hub/interfaces/shareable/info/.htaccess b/application/hub/interfaces/shareable/info/.htaccess new file mode 100644 index 000000000..3a4288278 --- /dev/null +++ b/application/hub/interfaces/shareable/info/.htaccess @@ -0,0 +1 @@ +Deny from all diff --git a/application/hub/interfaces/shareable/info/class_ShareableInfo.php b/application/hub/interfaces/shareable/info/class_ShareableInfo.php new file mode 100644 index 000000000..44121242a --- /dev/null +++ b/application/hub/interfaces/shareable/info/class_ShareableInfo.php @@ -0,0 +1,48 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Hub 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 . + */ +interface ShareableInfo extends FrameworkInterface { + /** + * Fills the information class with data from a Listenable instance + * + * @param $listenerInstance An instance of a Listenable class + * @return void + */ + function fillWithListenerInformation (Listenable $listenerInstance); + + /** + * Fills the information class with data from a ConnectionHelper instance + * + * @param $helperInstance An instance of a ConnectionHelper class + * @return void + */ + function fillWithConnectionHelperInformation (ConnectionHelper $helperInstance); + +} diff --git a/application/hub/interfaces/visitor/pool/class_PoolVisitor.php b/application/hub/interfaces/visitor/pool/class_PoolVisitor.php new file mode 100644 index 000000000..833ef4e19 --- /dev/null +++ b/application/hub/interfaces/visitor/pool/class_PoolVisitor.php @@ -0,0 +1,40 @@ + + * @version 0.0.0 + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Hub 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 . + */ +interface PoolVisitor extends Visitor { + /** + * Visits the given pool instance + * + * @param $poolInstance A Poolable instance + * @return void + */ + function visitPool (Poolable $poolInstance); + +} diff --git a/core b/core index 8e72c59c6..197d364f6 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 8e72c59c6c3536da832917acf4f2a3903339ad19 +Subproject commit 197d364f62d39e6f731ff62001ac6cafccc0d9cc