* @version 0.0.0 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2015 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 BaseDht extends BaseHubSystem implements Distributable { /** * "Cached" instance of a publish helper */ private $publishHelperInstance = NULL; /** * Stacker name for "INSERT" node data */ const STACKER_NAME_INSERT_NODE = 'dht_insert_node'; const STACKER_NAME_PENDING_PUBLISHING = 'dht_pending_publish'; /** * Protected constructor * * @param $className Name of the class * @return void */ protected function __construct ($className) { // Call parent constructor parent::__construct($className); // Get a stacker instance for this DHT $stackInstance = ObjectFactory::createObjectByConfiguredName('dht_stacker_class'); // Set it in this class $this->setStackInstance($stackInstance); // Init all stackers $this->initStacks(); // Get the state factory and create the initial state. DhtStateFactory::createDhtStateInstanceByName('init', $this); } /** * Initializes all stackers * * @return void */ private function initStacks () { // Initialize all stacker $this->getStackInstance()->initStacks(array( self::STACKER_NAME_INSERT_NODE, self::STACKER_NAME_PENDING_PUBLISHING, )); } /** * Registers/updates an entry in the DHT with given data from $dhtData * array. Different DHT implemtations may handle this differently as they * may enrich the data with more meta data. * * @param $dhtData A valid array with DHT-related data (e.g. node/peer data) * @return void */ protected abstract function insertDataIntoDht (array $dhtData); /** * Updates/refreshes DHT data (e.g. status). * * @return void * @todo Find more to do here */ public function updateDhtData () { // Set some dummy configuration entries, e.g. dht_status $this->getConfigInstance()->setConfigEntry('dht_status', $this->getStateInstance()->getStateName()); } /** * Checks whether there are entries in "INSERT" node data stack * * @return $isPending Whether there are pending entries */ public function ifInsertNodeDataPending () { // Determine it if it is not empty $isPending = ($this->getStackInstance()->isStackEmpty(self::STACKER_NAME_INSERT_NODE) === FALSE); // Return status return $isPending; } /** * Inserts a single entry of node data into the DHT * * @return void */ public function insertSingleNodeData () { // Get next node data from stack $nodeData = $this->getStackInstance()->popNamed(self::STACKER_NAME_INSERT_NODE); // Make sure $nodeData is really an array and has at least one entry assert((is_array($nodeData)) && (count($nodeData) > 0)); // Insert the data $this->insertDataIntoDht($nodeData); } /** * Checks whether there are unpublished entries * * @return $hasUnpublished Whether there are unpublished entries * @todo Add minimum/maximum age limitations */ public function hasUnpublishedEntries () { // Call method on database wrapper $hasUnpublished = $this->getWrapperInstance()->hasUnpublishedEntries(); // Return it return $hasUnpublished; } /** * Initializes publication of DHT entries. This does only prepare * publication. The next step is to pickup such prepared entries and publish * them by uploading to other (recently appeared) DHT members. * * @return void */ public function initEntryPublication () { // Call method on database wrapper $this->getWrapperInstance()->initEntryPublication(); // Get result instance $resultInstance = $this->getWrapperInstance()->getUnpublishedEntriesInstance(); // Make sure the result instance is valid assert($resultInstance instanceof SearchableResult); assert($resultInstance->valid()); // "Walk" through all entries while ($resultInstance->next()) { // Get current entry $current = $resultInstance->current(); // Make sure only valid entries pass // @TODO Maybe add more small checks? assert(is_array($current)); // ... and push it to the next stack //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('BASE-DHT[' . __METHOD__ . ':' . __LINE__ . '] Pushing entry with ' . count($current) . ' elements to stack ' . self::STACKER_NAME_PENDING_PUBLISHING . ' ...'); $this->getStackInstance()->pushNamed(self::STACKER_NAME_PENDING_PUBLISHING, $current); } // END - while } /** * Checks whether there are entries pending publication * * @return $isPending Whether there are entries pending publication */ public function hasEntriesPendingPublication () { // Determine it if it is not empty $isPending = ($this->getStackInstance()->isStackEmpty(self::STACKER_NAME_PENDING_PUBLISHING) === FALSE); // Return status /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('BASE-DHT[' . __METHOD__ . ':' . __LINE__ . '] isPending=' . intval($isPending)); return $isPending; } /** * Whether this DHT's state is 'booting' * * @return $isBooting Whether this DHT is currently booting */ public function ifDhtIsBooting () { // Call state instance $isBooting = $this->getStateInstance()->ifDhtIsBooting(); // Return status /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('BASE-DHT[' . __METHOD__ . ':' . __LINE__ . '] isBooting=' . intval($isBooting)); return $isBooting; } /** * Publishes next entry found in stack. This method shall also update the * corresponding dabase entry. * * @return void * @todo Find out if loadDescriptorXml() can be called only once to avoid a lot methods working. */ public function publishEntry () { // This test must not fail assert($this->hasEntriesPendingPublication()); // Is there an instance? if (!$this->publishHelperInstance instanceof HelpableDht) { // Get a helper instance $this->publishHelperInstance = ObjectFactory::createObjectByConfiguredName('dht_publish_entry_helper_class'); } // END - if // Load the announcement descriptor $this->publishHelperInstance->loadDescriptorXml($this); // "Pop" next entry $entry = $this->getStackInstance()->popNamed(self::STACKER_NAME_PENDING_PUBLISHING); // Some sanity-checks assert(is_array($entry)); // Remove any non-public data the database layer desires //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DHT[' . __METHOD__ . ':' . __LINE__ . ']: Calling this->getWrapperInstance()->removeNonPublicDataFromArray(data) ...'); $entry = $this->getWrapperInstance()->removeNonPublicDataFromArray($entry); //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DHT[' . __METHOD__ . ':' . __LINE__ . ']: entry[]=' . gettype($entry)); // Some sanity-checks again assert(is_array($entry)); // Assign multiple variables $this->publishHelperInstance->getTemplateInstance()->assignMultipleVariables($entry); // "Publish" the descriptor by sending it to the bootstrap/list nodes $this->publishHelperInstance->sendPackage($this); } /** * Whether the DHT has fully bootstrapped (after state 'booting') * * @return $isFullyBooted Whether the DHT is fully booted * @todo 0% done */ public function hasFullyBootstrapped () { // Get state and check it $this->partialStub('Please implement this method.'); } /** * Enable DHT bootstrap request acceptance for local node * * @return void * @todo Switch flag 'accept_bootstrap' */ public function enableAcceptDhtBootstrap () { // Call method on database wrapper $this->getWrapperInstance()->enableAcceptDhtBootstrap(); } } // [EOF] ?>