* @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 = ''; /** * 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; } /** * 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()->readConfig('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->getDebugInstance()->output(__FUNCTION__.'['.__LINE__.']: IP matches remote address ' . $ipPort . '.'); // Stop further searching break; } elseif ($ipPortArray[0] == $this->getConfigInstance()->readConfig('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->getDebugInstance()->output(__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->getDebugInstance()->output(' '); $this->getDebugInstance()->output($app->getAppName() . ' v' . $app->getAppVersion() . ' - ' . $this->getRequestInstance()->getRequestElement('mode') . ' mode active'); $this->getDebugInstance()->output('Copyright (c) 2007 - 2008 Roland Haeder, 2009 Hub Developer Team'); $this->getDebugInstance()->output(' '); $this->getDebugInstance()->output('This program comes with ABSOLUTELY NO WARRANTY; for details see docs/COPYING.'); $this->getDebugInstance()->output('This is free software, and you are welcome to redistribute it under certain'); $this->getDebugInstance()->output('conditions; see docs/COPYING for details.'); $this->getDebugInstance()->output(' '); } /** * Do generic things for bootup phase. This can be e.g. checking if the * right node mode is selected for this hub's IP number. * * @return void * @todo This method is maybe not yet finished. */ protected function doGenericBootstrapping () { // --------------------- Hub-id acquirement phase --------------------- // Acquire a hub-id. This step generates on first launch a new one and // on any later launches it will restore the hub-id from the database. // A passed 'nickname=xxx' argument will be used to add some // 'personality' to the hub. $this->bootstrapAcquireHubId(); // ------------------- More generic bootstrap steps ------------------- // Generate the session id which will only be stored in RAM and kept for // the whole "session". $this->bootstrapGenerateSessionId(); // Restore a previously downloaded bootstrap-node list. $this->bootstrapRestoreNodeList(); // @TODO Add some generic bootstrap steps $this->partialStub('Add some generic bootstrap steps here.'); } /** * 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 */ private 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->getDebugInstance()->output('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->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->getDebugInstance()->output('BOOTSTRAP: Created new node-id: ' . $this->getNodeId() . ''); } } /** * Initializes queues which every node needs * * @return void */ protected function initGenericQueues () { // Set it $this->queryInstance = ObjectFactory::createObjectByConfiguredName('query_connector_class', array($this)); } /** * 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()); } /** * 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; } /** * Activates the hub by doing some final preparation and setting * $hubIsActive to true * * @return void */ public function activateHub () { // Checks wether a listener is still active and shuts it down if one // is still listening if (($this->checkIfListenerIsActive()) && ($this->isHubActive())) { // Shutdown them down before they can hurt anything $this->shutdownListenerPool(); } // END - if // Initialize the TCP/UDP listener pool $this->initializeListenerPool(); // @TODO Do some final preparation $this->partialStub('Do some final preparation before the hub gots activated.'); // ----------------------- Last step from here ------------------------ // Activate the hub. This is ALWAYS the last step in this method $this->hubIsActive = true; // ---------------------- Last step until here ------------------------ } /** * Initializes the listener pool (class) * * @return void */ private function initializeListenerPool () { // Get a new pool instance $this->listenerPoolInstance = ObjectFactory::createObjectByConfiguredName('listener_pool_class', array($this)); // Initialize the TCP 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(); // Add this listener to the pool $this->listenerPoolInstance->addListener($listenerInstance); // Initialize the UDP 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(); // Add this listener to the pool $this->listenerPoolInstance->addListener($listenerInstance); } } // [EOF] ?>