]> git.mxchange.org Git - hub.git/blob - application/hub/main/dht/class_BaseDht.php
f4e4a0344acea228ad57b7786fcda5690741c393
[hub.git] / application / hub / main / dht / class_BaseDht.php
1 <?php
2 /**
3  * A general DHT class
4  *
5  * @author              Roland Haeder <webmaster@shipsimu.org>
6  * @version             0.0.0
7  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2012 Hub Developer Team
8  * @license             GNU GPL 3.0 or any newer version
9  * @link                http://www.shipsimu.org
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24 abstract class BaseDht extends BaseHubSystem implements Distributable {
25         /**
26          * "Cached" instance of a publish helper
27          */
28         private $publishHelperInstance = NULL;
29
30         /**
31          * Stacker name for "INSERT" node data
32          */
33         const STACKER_NAME_INSERT_NODE        = 'dht_insert_node';
34         const STACKER_NAME_PENDING_PUBLISHING = 'dht_pending_publish';
35
36         /**
37          * Protected constructor
38          *
39          * @param       $className      Name of the class
40          * @return      void
41          */
42         protected function __construct ($className) {
43                 // Call parent constructor
44                 parent::__construct($className);
45
46                 // Get a stacker instance for this DHT
47                 $stackerInstance = ObjectFactory::createObjectByConfiguredName('dht_stacker_class');
48
49                 // Set it in this class
50                 $this->setStackerInstance($stackerInstance);
51
52                 // Init all stackers
53                 $this->initStacks();
54
55                 /*
56                  * Get the state factory and create the initial state, we don't need
57                  * the state instance here
58                  */
59                 DhtStateFactory::createDhtStateInstanceByName('init', $this);
60         }
61
62         /**
63          * Initializes all stackers
64          *
65          * @return      void
66          */
67         private function initStacks () {
68                 // Initialize all stacker
69                 $this->getStackerInstance()->initStacks(array(
70                         self::STACKER_NAME_INSERT_NODE,
71                         self::STACKER_NAME_PENDING_PUBLISHING,
72                 ));
73         }
74
75         /**
76          * Registers/updates an entry in the DHT with given data from $dhtData
77          * array. Different DHT implemtations may handle this differently as they
78          * may enrich the data with more meta data.
79          *
80          * @param       $dhtData        A valid array with DHT-related data (e.g. node/peer data)
81          * @return      void
82          */
83         protected abstract function insertDataIntoDht (array $dhtData);
84
85         /**
86          * Updates/refreshes DHT data (e.g. status).
87          *
88          * @return      void
89          * @todo        Find more to do here
90          */
91         public function updateDhtData () {
92                 // Set some dummy configuration entries, e.g. dht_status
93                 $this->getConfigInstance()->setConfigEntry('dht_status', $this->getStateInstance()->getStateName());
94         }
95
96         /**
97          * Checks whether there are entries in "INSERT" node data stack
98          *
99          * @return      $isPending      Whether there are pending entries
100          */
101         public function ifInsertNodeDataPending () {
102                 // Determine it if it is not empty
103                 $isPending = ($this->getStackerInstance()->isStackEmpty(self::STACKER_NAME_INSERT_NODE) === FALSE);
104
105                 // Return status
106                 return $isPending;
107         }
108
109         /**
110          * Inserts a single entry of node data into the DHT
111          *
112          * @return      void
113          */
114         public function insertSingleNodeData () {
115                 // Get next node data from stack
116                 $nodeData = $this->getStackerInstance()->popNamed(self::STACKER_NAME_INSERT_NODE);
117
118                 // Make sure $nodeData is really an array and has at least one entry
119                 assert((is_array($nodeData)) && (count($nodeData) > 0));
120
121                 // Insert the data
122                 $this->insertDataIntoDht($nodeData);
123         }
124
125         /**
126          * Checks whether there are unpublished entries
127          *
128          * @return      $hasUnpublished         Whether there are unpublished entries
129          * @todo        Add minimum/maximum age limitations
130          */
131         public function hasUnpublishedEntries () {
132                 // Call method on database wrapper
133                 $hasUnpublished = $this->getWrapperInstance()->hasUnpublishedEntries();
134
135                 // Return it
136                 return $hasUnpublished;
137         }
138
139         /**
140          * Initializes publication of DHT entries. This does only prepare
141          * publication. The next step is to pickup such prepared entries and publish
142          * them by uploading to other (recently appeared) DHT members.
143          *
144          * @return      void
145          */
146         public function initEntryPublication () {
147                 // Call method on database wrapper
148                 $this->getWrapperInstance()->initEntryPublication();
149
150                 // Get result instance
151                 $resultInstance = $this->getWrapperInstance()->getUnpublishedEntriesInstance();
152
153                 // Make sure the result instance is valid
154                 assert($resultInstance instanceof SearchableResult);
155                 assert($resultInstance->valid());
156
157                 // "Walk" through all entries
158                 while ($resultInstance->next()) {
159                         // Get current entry
160                         $current = $resultInstance->current();
161
162                         // Make sure only valid entries pass
163                         // @TODO Maybe add more small checks?
164                         assert(is_array($current));
165
166                         // ... and push it to the next stack
167                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('BASE-DHT[' . __METHOD__ . ':' . __LINE__ . '] Pushing entry with ' . count($current) . ' elements to stack ' . self::STACKER_NAME_PENDING_PUBLISHING . ' ...');
168                         $this->getStackerInstance()->pushNamed(self::STACKER_NAME_PENDING_PUBLISHING, $current);
169                 } // END - while
170         }
171
172         /**
173          * Checks whether there are entries pending publication
174          *
175          * @return      $isPending      Whether there are entries pending publication
176          */
177         public function hasEntriesPendingPublication () {
178                 // Determine it if it is not empty
179                 $isPending = ($this->getStackerInstance()->isStackEmpty(self::STACKER_NAME_PENDING_PUBLISHING) === FALSE);
180
181                 // Return status
182                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('BASE-DHT[' . __METHOD__ . ':' . __LINE__ . '] isPending=' . intval($isPending));
183                 return $isPending;
184         }
185
186         /**
187          * Whether this DHT's state is 'booting'
188          *
189          * @return      $isBooting      Whether this DHT is currently booting
190          */
191         public function ifDhtIsBooting () {
192                 // Call state instance
193                 $isBooting = $this->getStateInstance()->ifDhtIsBooting();
194
195                 // Return status
196                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('BASE-DHT[' . __METHOD__ . ':' . __LINE__ . '] isBooting=' . intval($isBooting));
197                 return $isBooting;
198         }
199
200         /**
201          * Publishes next entry found in stack. This method shall also update the
202          * corresponding dabase entry.
203          *
204          * @return      void
205          * @todo        Find out if loadDescriptorXml() can be called only once to avoid a lot methods working.
206          */
207         public function publishEntry () {
208                 // This test must not fail
209                 assert($this->hasEntriesPendingPublication());
210
211                 // Is there an instance?
212                 if (!$this->publishHelperInstance instanceof HelpableDht) {
213                         // Get a helper instance
214                         $this->publishHelperInstance = ObjectFactory::createObjectByConfiguredName('dht_publish_entry_helper_class');
215                 } // END - if
216
217                 // Load the announcement descriptor
218                 $this->publishHelperInstance->loadDescriptorXml($this);
219
220                 // "Pop" next entry
221                 $entry = $this->getStackerInstance()->popNamed(self::STACKER_NAME_PENDING_PUBLISHING);
222
223                 // Some sanity-checks
224                 assert(is_array($entry));
225
226                 // Remove any non-public data the database layer desires
227                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DHT[' . __METHOD__ . ':' . __LINE__ . ']: Calling this->getWrapperInstance()->removeNonPublicDataFromArray(data) ...');
228                 $entry = $this->getWrapperInstance()->removeNonPublicDataFromArray($entry);
229                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DHT[' . __METHOD__ . ':' . __LINE__ . ']: entry[]=' . gettype($entry));
230
231                 // Some sanity-checks again
232                 assert(is_array($entry));
233
234                 // Inject variables
235                 $this->publishHelperInstance->getTemplateInstance()->injectConfigVariables($entry);
236
237                 // "Publish" the descriptor by sending it to the bootstrap/list nodes
238                 $this->publishHelperInstance->sendPackage($this);
239         }
240
241         /**
242          * Whether the DHT has fully bootstrapped (after state 'booting')
243          *
244          * @return      $isFullyBooted  Whether the DHT is fully booted
245          * @todo        0% done
246          */
247         public function hasFullyBootstrapped () {
248                 // Get state and check it
249                 $this->partialStub('Please implement this method.');
250         }
251
252         /**
253          * Enable DHT bootstrap request acceptance for local node
254          *
255          * @return      void
256          * @todo        Switch flag 'accept_bootstrap'
257          */
258         public function enableAcceptDhtBootstrap () {
259                 // Call method on database wrapper
260                 $this->getWrapperInstance()->enableAcceptDhtBootstrap();
261         }
262 }
263
264 // [EOF]
265 ?>