* @version 0.0.0 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 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 BaseHubNode extends BaseHubSystem implements Updateable { /** * Node id */ private $nodeId = ''; /** * Session id */ private $sessionId = ''; /** * IP/port number of bootstrapping node */ private $bootIpPort = ''; /** * Query connector instance */ private $queryInstance = null; /** * Listener pool instance */ private $listenerPoolInstance = null; /** * Wether the hub is active (true/false) */ private $hubIsActive = false; /** * Protected constructor * * @param $className Name of the class * @return void */ protected function __construct ($className) { // Call parent constructor parent::__construct($className); } /** * Setter for node id * * @param $nodeId Our new node id * @return void */ private final function setNodeId ($nodeId) { $this->nodeId = (string) $nodeId; } /** * Getter for node id * * @return $nodeId Our new node id */ private final function getNodeId () { return $this->nodeId; } /** * Setter for listener pool instance * * @param $listenerPoolInstance Our new listener pool instance * @return void */ private final function setListenerPoolInstance (PoolableListener $listenerPoolInstance) { $this->listenerPoolInstance = $listenerPoolInstance; } /** * Getter for listener pool instance * * @return $listenerPoolInstance Our current listener pool instance */ public final function getListenerPoolInstance () { return $this->listenerPoolInstance; } /** * Setter for session id * * @param $sessionId Our new session id * @return void */ private final function setSessionId ($sessionId) { $this->sessionId = (string) $sessionId; } /** * Getter for session id * * @return $sessionId Our new session id */ public final function getSessionId () { return $this->sessionId; } /** * Setter for query instance * * @param $queryInstance Our new query instance * @return void */ private final function setQueryInstance (Queryable $queryInstance) { $this->queryInstance = $queryInstance; } /** * Getter for query instance * * @return $queryInstance Our new query instance */ public final function getQueryInstance () { return $this->queryInstance; } /** * Getter for boot IP/port combination * * @return $bootIpPort The IP/port combination of the boot node */ protected final function getBootIpPort () { return $this->bootIpPort; } /** * Checks wether the given IP address matches one of the bootstrapping nodes * * @param $remoteAddr IP address to checkout against our bootstrapping list * @return $isFound Wether the IP is found */ protected function ifAddressMatchesBootstrappingNodes ($remoteAddr) { // By default nothing is found $isFound = false; // Run through all configured IPs foreach (explode(',', $this->getConfigInstance()->getConfigEntry('hub_bootstrap_nodes')) as $ipPort) { // Split it up in IP/port $ipPortArray = explode(':', $ipPort); // Does it match? if ($ipPortArray[0] == $remoteAddr) { // Found it! $isFound = true; // Remember the port number $this->bootIpPort = $ipPort; // Output message $this->debugOutput('BOOTSTRAP: ' . __FUNCTION__ . '[' . __LINE__ . ']: IP matches remote address ' . $ipPort . '.'); // Stop further searching break; } elseif ($ipPortArray[0] == $this->getConfigInstance()->getConfigEntry('node_listen_addr')) { // IP matches listen address. At this point we really don't care // if we can also listen on that address! $isFound = true; // Remember the port number $this->bootIpPort = $ipPort; // Output message $this->debugOutput('BOOTSTRAP: ' . __FUNCTION__ . '[' . __LINE__ . ']: IP matches listen address ' . $ipPort . '.'); // Stop further searching break; } } // END - foreach // Return the result return $isFound; } /** * Outputs the console teaser. This should only be executed on startup or * full restarts. This method generates some space around the teaser. * * @return void */ public function outputConsoleTeaser () { // Get the app instance (for shortening our code) $app = $this->getApplicationInstance(); // Output all lines $this->debugOutput(' '); $this->debugOutput($app->getAppName() . ' v' . $app->getAppVersion() . ' - ' . $this->getRequestInstance()->getRequestElement('mode') . ' mode active'); $this->debugOutput('Copyright (c) 2007 - 2008 Roland Haeder, 2009 Hub Developer Team'); $this->debugOutput(' '); $this->debugOutput('This program comes with ABSOLUTELY NO WARRANTY; for details see docs/COPYING.'); $this->debugOutput('This is free software, and you are welcome to redistribute it under certain'); $this->debugOutput('conditions; see docs/COPYING for details.'); $this->debugOutput(' '); } /** * Generic method to acquire a hub-id. On first run this generates a new one * based on many pseudo-random data. On any later run, unless the id * got not removed from database, it will be restored from the database. * * @return void */ public function bootstrapAcquireHubId () { // Get a wrapper instance $wrapperInstance = ObjectFactory::createObjectByConfiguredName('node_info_db_wrapper_class'); // Now get a search criteria instance $searchInstance = ObjectFactory::createObjectByConfiguredName('search_criteria_class'); // Search for the node number zero which is hard-coded the default $searchInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_NR, 1); $searchInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_TYPE, $this->getRequestInstance()->getRequestElement('mode')); $searchInstance->setLimit(1); // Get a result back $resultInstance = $wrapperInstance->doSelectByCriteria($searchInstance); // Is it valid? if ($resultInstance->next()) { // Save the result instance in this class $this->setResultInstance($resultInstance); // Get the node id from result and set it $this->setNodeId($this->getField(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_ID)); // Output message $this->debugOutput('BOOTSTRAP: Re-using found node-id: ' . $this->getNodeId() . ''); } else { // Get an RNG instance (Random Number Generator) $rngInstance = ObjectFactory::createObjectByConfiguredName('rng_class'); // Generate a pseudo-random string $randomString = $rngInstance->randomString(255) . ':' . $this->getBootIpPort() . ':' . $this->getRequestInstance()->getRequestElement('mode'); // Get a crypto instance $cryptoInstance = ObjectFactory::createObjectByConfiguredName('crypto_class'); // Hash and encrypt the string so we become a "node id" aka Hub-Id $this->setNodeId($cryptoInstance->hashString($cryptoInstance->encryptString($randomString))); // Register the node id with our wrapper $wrapperInstance->registerNodeId($this, $this->getRequestInstance()); // Output message $this->debugOutput('BOOTSTRAP: Created new node-id: ' . $this->getNodeId() . ''); } } /** * Generates a session id which will be sent to the other hubs and clients * * @return void */ public function bootstrapGenerateSessionId () { // Get an RNG instance $rngInstance = ObjectFactory::createObjectByConfiguredName('rng_class'); // Generate a pseudo-random string $randomString = $rngInstance->randomString(255) . ':' . $this->getBootIpPort() . ':' . $this->getRequestInstance()->getRequestElement('mode'); // Get a crypto instance $cryptoInstance = ObjectFactory::createObjectByConfiguredName('crypto_class'); // Hash and encrypt the string so we become a "node id" aka Hub-Id $this->setSessionId($cryptoInstance->hashString($cryptoInstance->encryptString($randomString))); // Get a wrapper instance $wrapperInstance = ObjectFactory::createObjectByConfiguredName('node_info_db_wrapper_class'); // Register the node id with our wrapper $wrapperInstance->registerSessionId($this, $this->getRequestInstance()); // Output message $this->debugOutput('BOOTSTRAP: Created new session-id: ' . $this->getSessionId() . ''); } /** * Initializes queues which every node needs * * @return void */ protected function initGenericQueues () { // Debug message $this->debugOutput('BOOTSTRAP: Initialize queues: START'); // Set the query connector instance $this->setQueryInstance(ObjectFactory::createObjectByConfiguredName('query_connector_class', array($this))); // Run a test query $this->getQueryInstance()->doTestQuery(); // Debug message $this->debugOutput('BOOTSTRAP: Initialize queues: FINISHED'); } /** * Adds hub data elements to a given dataset instance * * @param $criteriaInstance An instance of a storeable criteria * @param $requestInstance An instance of a Requestable class * @return void */ public function addElementsToDataSet (StoreableCriteria $criteriaInstance, Requestable $requestInstance) { // Add node number and type $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_NR, 1); $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_TYPE, $requestInstance->getRequestElement('mode')); // Add the node id $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_ID, $this->getNodeId()); // Add the session id if acquired if ($this->getSessionId() != '') { $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_SESSION_ID, $this->getSessionId()); } // END - if } /** * Updates a given field with new value * * @param $fieldName Field to update * @param $fieldValue New value to store * @return void * @throws DatabaseUpdateSupportException If this class does not support database updates * @todo Try to make this method more generic so we can move it in BaseFrameworkSystem */ public function updateDatabaseField ($fieldName, $fieldValue) { // Unfinished $this->partialStub('Unfinished!'); return; // Get a critieria instance $searchInstance = ObjectFactory::createObjectByConfiguredName('search_criteria_class'); // Add search criteria $searchInstance->addCriteria(UserDatabaseWrapper::DB_COLUMN_USERNAME, $this->getUserName()); $searchInstance->setLimit(1); // Now get another criteria $updateInstance = ObjectFactory::createObjectByConfiguredName('update_criteria_class'); // Add criteria entry which we shall update $updateInstance->addCriteria($fieldName, $fieldValue); // Add the search criteria for searching for the right entry $updateInstance->setSearchInstance($searchInstance); // Set wrapper class name $updateInstance->setWrapperConfigEntry('user_db_wrapper_class'); // Remember the update in database result $this->getResultInstance()->add2UpdateQueue($updateInstance); } /** * Getter for $hubIsActive attribute * * @return $hubIsActive Wether the hub is activer */ public final function isHubActive () { return $this->hubIsActive; } /** * Setter for $hubIsActive attribute * * @param $hubIsActive Wether the hub is activer */ public final function enableHubIsActive ($hubIsActive = true) { $this->hubIsActive = $hubIsActive; } /** * Activates the hub by doing some final preparation and setting * $hubIsActive to true * * @param $requestInstance A Requestable class * @param $responseInstance A Responseable class * @return void */ public function activateHub (Requestable $requestInstance, Responseable $responseInstance) { // Checks wether a listener is still active and shuts it down if one // is still listening if (($this->determineIfListenerIsActive()) && ($this->isHubActive())) { // Shutdown them down before they can hurt anything $this->shutdownListenerPool(); } // END - if // Get the controller here $controllerInstance = Registry::getRegistry()->getInstance('controller'); // Run all filters for the hub activation $controllerInstance->executeActivationFilters($requestInstance, $responseInstance); // ----------------------- Last step from here ------------------------ // Activate the hub. This is ALWAYS the last step in this method $this->enableHubIsActive(); // ---------------------- Last step until here ------------------------ } /** * Initializes the listener pool (class) * * @return void */ public function initializeListenerPool () { // Debug output $this->debugOutput('HUB: Initialize listener: START'); // Get a new pool instance $this->setListenerPoolInstance(ObjectFactory::createObjectByConfiguredName('listener_pool_class', array($this))); // Get an instance of the low-level listener $listenerInstance = ObjectFactory::createObjectByConfiguredName('tcp_listener_class', array($this)); // Setup address and port $listenerInstance->setListenAddressByConfiguration('node_listen_addr'); $listenerInstance->setListenPortByConfiguration('node_tcp_listen_port'); // Initialize the listener $listenerInstance->initListener(); // Get a decorator class $decoratorInstance = ObjectFactory::createObjectByConfiguredName('hub_tcp_listener_class', array($listenerInstance)); // Add this listener to the pool $this->getListenerPoolInstance()->addListener($decoratorInstance); // Get a decorator class $decoratorInstance = ObjectFactory::createObjectByConfiguredName('client_tcp_listener_class', array($listenerInstance)); // Add this listener to the pool $this->getListenerPoolInstance()->addListener($decoratorInstance); // Get an instance of the low-level listener $listenerInstance = ObjectFactory::createObjectByConfiguredName('udp_listener_class', array($this)); // Setup address and port $listenerInstance->setListenAddressByConfiguration('node_listen_addr'); $listenerInstance->setListenPortByConfiguration('node_udp_listen_port'); // Initialize the listener $listenerInstance->initListener(); // Get a decorator class $decoratorInstance = ObjectFactory::createObjectByConfiguredName('hub_udp_listener_class', array($listenerInstance)); // Add this listener to the pool $this->getListenerPoolInstance()->addListener($decoratorInstance); // Get a decorator class $decoratorInstance = ObjectFactory::createObjectByConfiguredName('client_udp_listener_class', array($listenerInstance)); // Add this listener to the pool $this->getListenerPoolInstance()->addListener($decoratorInstance); // Debug output $this->debugOutput('HUB: Initialize listener: FINISHED.'); } /** * Restores a previously stored node list from database * * @return void */ public function bootstrapRestoreNodeList () { // Debug output $this->debugOutput('HUB: Restore node list: START'); // Get a wrapper instance $wrapperInstance = ObjectFactory::createObjectByConfiguredName('node_list_db_wrapper_class'); // Now get a search criteria instance $searchInstance = ObjectFactory::createObjectByConfiguredName('search_criteria_class'); // Search for the node number zero which is hard-coded the default // @TODO Add some criteria, e.g. if the node is active or so //$searchInstance->addCriteria(NodeListDatabaseWrapper::DB_COLUMN_NODE_NR, 1); //$searchInstance->addCriteria(NodeListDatabaseWrapper::DB_COLUMN_NODE_TYPE, $this->getRequestInstance()->getRequestElement('mode')); //$searchInstance->setLimit(1); // Get a result back $resultInstance = $wrapperInstance->doSelectByCriteria($searchInstance); // Is it valid? if ($resultInstance->next()) { $this->partialStub('Do something for restoring the list.'); // Output message //$this->debugOutput('BOOTSTRAP: '); } // END - if // Debug output $this->debugOutput('HUB: Restore node list: FINISHED.'); } } // [EOF] ?>