Updated 'core' + renamed 'main' -> 'classes'.
[hub.git] / application / hub / classes / 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 - 2015 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                 $stackInstance = ObjectFactory::createObjectByConfiguredName('dht_stacker_class');
48
49                 // Set it in this class
50                 $this->setStackInstance($stackInstance);
51
52                 // Init all stackers
53                 $this->initStacks();
54
55                 // Get the state factory and create the initial state.
56                 DhtStateFactory::createDhtStateInstanceByName('init', $this);
57         }
58
59         /**
60          * Initializes all stackers
61          *
62          * @return      void
63          */
64         private function initStacks () {
65                 // Initialize all stacker
66                 $this->getStackInstance()->initStacks(array(
67                         self::STACKER_NAME_INSERT_NODE,
68                         self::STACKER_NAME_PENDING_PUBLISHING,
69                 ));
70         }
71
72         /**
73          * Registers/updates an entry in the DHT with given data from $dhtData
74          * array. Different DHT implemtations may handle this differently as they
75          * may enrich the data with more meta data.
76          *
77          * @param       $dhtData        A valid array with DHT-related data (e.g. node/peer data)
78          * @return      void
79          */
80         protected abstract function insertDataIntoDht (array $dhtData);
81
82         /**
83          * Updates/refreshes DHT data (e.g. status).
84          *
85          * @return      void
86          * @todo        Find more to do here
87          */
88         public function updateDhtData () {
89                 // Set some dummy configuration entries, e.g. dht_status
90                 $this->getConfigInstance()->setConfigEntry('dht_status', $this->getStateInstance()->getStateName());
91         }
92
93         /**
94          * Checks whether there are entries in "INSERT" node data stack
95          *
96          * @return      $isPending      Whether there are pending entries
97          */
98         public function ifInsertNodeDataPending () {
99                 // Determine it if it is not empty
100                 $isPending = ($this->getStackInstance()->isStackEmpty(self::STACKER_NAME_INSERT_NODE) === FALSE);
101
102                 // Return status
103                 return $isPending;
104         }
105
106         /**
107          * Inserts a single entry of node data into the DHT
108          *
109          * @return      void
110          */
111         public function insertSingleNodeData () {
112                 // Get next node data from stack
113                 $nodeData = $this->getStackInstance()->popNamed(self::STACKER_NAME_INSERT_NODE);
114
115                 // Make sure $nodeData is really an array and has at least one entry
116                 assert((is_array($nodeData)) && (count($nodeData) > 0));
117
118                 // Insert the data
119                 $this->insertDataIntoDht($nodeData);
120         }
121
122         /**
123          * Checks whether there are unpublished entries
124          *
125          * @return      $hasUnpublished         Whether there are unpublished entries
126          * @todo        Add minimum/maximum age limitations
127          */
128         public function hasUnpublishedEntries () {
129                 // Call method on database wrapper
130                 $hasUnpublished = $this->getWrapperInstance()->hasUnpublishedEntries();
131
132                 // Return it
133                 return $hasUnpublished;
134         }
135
136         /**
137          * Initializes publication of DHT entries. This does only prepare
138          * publication. The next step is to pickup such prepared entries and publish
139          * them by uploading to other (recently appeared) DHT members.
140          *
141          * @return      void
142          */
143         public function initEntryPublication () {
144                 // Call method on database wrapper
145                 $this->getWrapperInstance()->initEntryPublication();
146
147                 // Get result instance
148                 $resultInstance = $this->getWrapperInstance()->getUnpublishedEntriesInstance();
149
150                 // Make sure the result instance is valid
151                 assert($resultInstance instanceof SearchableResult);
152                 assert($resultInstance->valid());
153
154                 // "Walk" through all entries
155                 while ($resultInstance->next()) {
156                         // Get current entry
157                         $current = $resultInstance->current();
158
159                         // Make sure only valid entries pass
160                         // @TODO Maybe add more small checks?
161                         assert(is_array($current));
162
163                         // ... and push it to the next stack
164                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('BASE-DHT[' . __METHOD__ . ':' . __LINE__ . '] Pushing entry with ' . count($current) . ' elements to stack ' . self::STACKER_NAME_PENDING_PUBLISHING . ' ...');
165                         $this->getStackInstance()->pushNamed(self::STACKER_NAME_PENDING_PUBLISHING, $current);
166                 } // END - while
167         }
168
169         /**
170          * Checks whether there are entries pending publication
171          *
172          * @return      $isPending      Whether there are entries pending publication
173          */
174         public function hasEntriesPendingPublication () {
175                 // Determine it if it is not empty
176                 $isPending = ($this->getStackInstance()->isStackEmpty(self::STACKER_NAME_PENDING_PUBLISHING) === FALSE);
177
178                 // Return status
179                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('BASE-DHT[' . __METHOD__ . ':' . __LINE__ . '] isPending=' . intval($isPending));
180                 return $isPending;
181         }
182
183         /**
184          * Whether this DHT's state is 'booting'
185          *
186          * @return      $isBooting      Whether this DHT is currently booting
187          */
188         public function ifDhtIsBooting () {
189                 // Call state instance
190                 $isBooting = $this->getStateInstance()->ifDhtIsBooting();
191
192                 // Return status
193                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('BASE-DHT[' . __METHOD__ . ':' . __LINE__ . '] isBooting=' . intval($isBooting));
194                 return $isBooting;
195         }
196
197         /**
198          * Publishes next entry found in stack. This method shall also update the
199          * corresponding dabase entry.
200          *
201          * @return      void
202          * @todo        Find out if loadDescriptorXml() can be called only once to avoid a lot methods working.
203          */
204         public function publishEntry () {
205                 // This test must not fail
206                 assert($this->hasEntriesPendingPublication());
207
208                 // Is there an instance?
209                 if (!$this->publishHelperInstance instanceof HelpableDht) {
210                         // Get a helper instance
211                         $this->publishHelperInstance = ObjectFactory::createObjectByConfiguredName('dht_publish_entry_helper_class');
212                 } // END - if
213
214                 // Load the announcement descriptor
215                 $this->publishHelperInstance->loadDescriptorXml($this);
216
217                 // "Pop" next entry
218                 $entry = $this->getStackInstance()->popNamed(self::STACKER_NAME_PENDING_PUBLISHING);
219
220                 // Some sanity-checks
221                 assert(is_array($entry));
222
223                 // Remove any non-public data the database layer desires
224                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DHT[' . __METHOD__ . ':' . __LINE__ . ']: Calling this->getWrapperInstance()->removeNonPublicDataFromArray(data) ...');
225                 $entry = $this->getWrapperInstance()->removeNonPublicDataFromArray($entry);
226                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DHT[' . __METHOD__ . ':' . __LINE__ . ']: entry[]=' . gettype($entry));
227
228                 // Some sanity-checks again
229                 assert(is_array($entry));
230
231                 // Assign multiple variables
232                 $this->publishHelperInstance->getTemplateInstance()->assignMultipleVariables($entry);
233
234                 // "Publish" the descriptor by sending it to the bootstrap/list nodes
235                 $this->publishHelperInstance->sendPackage($this);
236         }
237
238         /**
239          * Whether the DHT has fully bootstrapped (after state 'booting')
240          *
241          * @return      $isFullyBooted  Whether the DHT is fully booted
242          * @todo        0% done
243          */
244         public function hasFullyBootstrapped () {
245                 // Get state and check it
246                 $this->partialStub('Please implement this method.');
247         }
248
249         /**
250          * Enable DHT bootstrap request acceptance for local node
251          *
252          * @return      void
253          * @todo        Switch flag 'accept_bootstrap'
254          */
255         public function enableAcceptDhtBootstrap () {
256                 // Call method on database wrapper
257                 $this->getWrapperInstance()->enableAcceptDhtBootstrap();
258         }
259 }
260
261 // [EOF]
262 ?>