]> git.mxchange.org Git - hub.git/blob - application/hub/main/database/wrapper/node/class_NodeDistributedHashTableDatabaseWrapper.php
Updating node data in local DHT (not yet distributed here, that will do an other...
[hub.git] / application / hub / main / database / wrapper / node / class_NodeDistributedHashTableDatabaseWrapper.php
1 <?php
2 /**
3  * A database wrapper for distributed hash tables
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 class NodeDistributedHashTableDatabaseWrapper extends BaseDatabaseWrapper implements NodeDhtWrapper, Registerable {
25         // Constants for database table names
26         const DB_TABLE_NODE_DHT = 'node_dht';
27
28         // Constants for database column names
29         const DB_COLUMN_NODE_ID          = 'node_id';
30         const DB_COLUMN_SESSION_ID       = 'session_id';
31         const DB_COLUMN_EXTERNAL_IP      = 'external_ip';
32         const DB_COLUMN_LISTEN_PORT      = 'listen_port';
33         const DB_COLUMN_PRIVATE_KEY_HASH = 'private_key_hash';
34         const DB_COLUMN_NODE_MODE        = 'node_mode';
35         const DB_COLUMN_ACCEPTED_OBJECTS = 'accepted_object_types';
36         const DB_COLUMN_NODE_LIST        = 'node_list';
37
38         // Exception codes
39         const EXCEPTION_NODE_ALREADY_REGISTERED = 0x800;
40         const EXCEPTION_NODE_NOT_REGISTERED     = 0x801;
41
42         /**
43          * Protected constructor
44          *
45          * @return      void
46          */
47         protected function __construct () {
48                 // Call parent constructor
49                 parent::__construct(__CLASS__);
50         }
51
52         /**
53          * Creates an instance of this database wrapper by a provided user class
54          *
55          * @return      $wrapperInstance        An instance of the created wrapper class
56          */
57         public static final function createNodeDistributedHashTableDatabaseWrapper () {
58                 // Get a new instance
59                 $wrapperInstance = new NodeDistributedHashTableDatabaseWrapper();
60
61                 // Set (primary!) table name
62                 $wrapperInstance->setTableName(self::DB_TABLE_NODE_DHT);
63
64                 // Return the instance
65                 return $wrapperInstance;
66         }
67
68         /**
69          * Static getter for an array of all DHT database entries
70          *
71          * @return      $elements       All elements for the DHT dabase
72          */
73         public static final function getAllElements () {
74                 // Create array and ...
75                 $elements = array(
76                         self::DB_COLUMN_NODE_ID,
77                         self::DB_COLUMN_SESSION_ID,
78                         self::DB_COLUMN_EXTERNAL_IP,
79                         self::DB_COLUMN_LISTEN_PORT,
80                         self::DB_COLUMN_PRIVATE_KEY_HASH,
81                         self::DB_COLUMN_NODE_MODE,
82                         self::DB_COLUMN_ACCEPTED_OBJECTS,
83                         self::DB_COLUMN_NODE_LIST
84                 );
85
86                 // ... return it
87                 return $elements;
88         }
89
90         /**
91          * Prepares a search instance for given node data
92          *
93          * @param       $nodeData                       An array with valid node data
94          * @return      $searchInstance         An instance of a SearchCriteria class
95          */
96         private function prepareSearchInstance (array $nodeData) {
97                 // Assert on array elements
98                 assert(isset($nodeData[self::DB_COLUMN_NODE_ID]));
99
100                 // Get instance
101                 $searchInstance = ObjectFactory::createObjectByConfiguredName('search_criteria_class');
102
103                 // Search for node id and limit it to one entry
104                 $searchInstance->addCriteria(self::DB_COLUMN_NODE_ID, $nodeData[self::DB_COLUMN_NODE_ID]);
105                 $searchInstance->setLimit(1);
106
107                 // Return it
108                 return $searchInstance;
109         }
110
111         /**
112          * Prepares a "local" instance of a StoreableCriteria class with all node
113          * data for insert/update queries. This data set contains data from *this*
114          * (local) node.
115          *
116          * @return      $dataSetInstance        An instance of a StoreableCriteria class
117          */
118         private function prepareLocalDataSetInstance () {
119                 // Get node/request instances
120                 $nodeInstance = Registry::getRegistry()->getInstance('node');
121                 $requestInstance = ApplicationHelper::getSelfInstance()->getRequestInstance();
122
123                 // Get a dataset instance
124                 $dataSetInstance = ObjectFactory::createObjectByConfiguredName('dataset_criteria_class', array(self::DB_TABLE_NODE_DHT));
125
126                 // Set the primary key
127                 $dataSetInstance->setUniqueKey(self::DB_COLUMN_NODE_ID);
128
129                 // Get ip:port combination and "explode" it
130                 $ipPort = $nodeInstance->getAddressPortArray();
131
132                 // Make sure both is valid
133                 assert(($ipPort[0] !== 'invalid') && ($ipPort[1] !== 'invalid'));
134
135                 // Get an array of all accepted object types
136                 $objectList = $nodeInstance->getListFromAcceptedObjectTypes();
137
138                 // Make sure this is an array
139                 assert(is_array($objectList));
140
141                 // Add public node data
142                 $dataSetInstance->addCriteria(self::DB_COLUMN_NODE_MODE       , $requestInstance->getRequestElement('mode'));
143                 $dataSetInstance->addCriteria(self::DB_COLUMN_EXTERNAL_IP     , $ipPort[0]);
144                 $dataSetInstance->addCriteria(self::DB_COLUMN_LISTEN_PORT     , $ipPort[1]);
145                 $dataSetInstance->addCriteria(self::DB_COLUMN_NODE_ID         , $nodeInstance->getNodeId());
146                 $dataSetInstance->addCriteria(self::DB_COLUMN_SESSION_ID      , $nodeInstance->getSessionId());
147                 $dataSetInstance->addCriteria(self::DB_COLUMN_PRIVATE_KEY_HASH, $nodeInstance->getPrivateKeyHash());
148                 $dataSetInstance->addCriteria(self::DB_COLUMN_ACCEPTED_OBJECTS, implode(BaseHubNode::OBJECT_LIST_SEPARATOR, $objectList));
149
150                 // Return it
151                 return $dataSetInstance;
152         }
153
154         /**
155          * Checks whether the local (*this*) node is registered in the DHT by
156          * checking if the external ip/port is found.
157          *
158          * @return      $isRegistered   Whether *this* node is registered in the DHT
159          */
160         public function isLocalNodeRegistered () {
161                 // Is there cache?
162                 if (!isset($GLOBALS[__METHOD__])) {
163                         // Get a search criteria instance
164                         $searchInstance = ObjectFactory::createObjectByConfiguredName('search_criteria_class');
165
166                         // Get node instance
167                         $nodeInstance = Registry::getRegistry()->getInstance('node');
168
169                         // Get ip:port combination and "explode" it
170                         $ipPort = $nodeInstance->getAddressPortArray();
171
172                         /*
173                          * Make sure both is not 'invalid' which means that the resolver
174                          * didn't work.
175                          */
176                         assert(($ipPort[0] !== 'invalid') && ($ipPort[1] !== 'invalid'));
177
178                         // Add ip:port/node id as criteria
179                         $searchInstance->addCriteria(self::DB_COLUMN_EXTERNAL_IP, $ipPort[0]);
180                         $searchInstance->addCriteria(self::DB_COLUMN_LISTEN_PORT, $ipPort[1]);
181                         $searchInstance->addCriteria(self::DB_COLUMN_NODE_ID    , $nodeInstance->getNodeId());
182                         $searchInstance->setLimit(1);
183
184                         // Query database and get a result instance back
185                         $resultInstance = $this->doSelectByCriteria($searchInstance);
186
187                         // Cache result of if there is an entry, valid() will tell us if an entry is there
188                         $GLOBALS[__METHOD__] = $resultInstance->valid();
189                 } // END - if
190
191                 // Return result
192                 return $GLOBALS[__METHOD__];
193         }
194
195         /**
196          * Registeres the local (*this*) node with its data in the DHT.
197          *
198          * @return      void
199          */
200         public function registerLocalNode () {
201                 // Assert to make sure this method is called with no record in DB (the actual backend of the DHT)
202                 assert(!$this->isLocalNodeRegistered());
203
204                 // Get prepared data set instance
205                 $dataSetInstance = $this->prepareLocalDataSetInstance();
206
207                 // "Insert" this dataset instance completely into the database
208                 $this->queryInsertDataSet($dataSetInstance);
209         }
210
211         /**
212          * Updates local (*this*) node data in DHT, this is but not limited to the
213          * session id, ip number (and/or hostname) and port number.
214          *
215          * @return      void
216          */
217         public function updateLocalNode () {
218                 // Assert to make sure this method is called with one record in DB (the actual backend of the DHT)
219                 assert($this->isLocalNodeRegistered());
220
221                 // Get node instance
222                 $nodeInstance = Registry::getRegistry()->getInstance('node');
223
224                 // Get search criteria
225                 $searchInstance = ObjectFactory::createObjectByConfiguredName('search_criteria_class');
226
227                 // Search for node id and limit it to one entry
228                 $searchInstance->addCriteria(self::DB_COLUMN_NODE_ID, $nodeInstance->getNodeId());
229                 $searchInstance->setLimit(1);
230
231                 // Get a prepared dataset instance
232                 $dataSetInstance = $this->prepareLocalDataSetInstance();
233
234                 // Set search instance
235                 $dataSetInstance->setSearchInstance($searchInstance);
236
237                 // Update DHT database record
238                 $this->queryUpdateDataSet($dataSetInstance);
239         }
240
241         /**
242          * Finds a node locally by given session id
243          *
244          * @param       $sessionId      Session id to lookup
245          * @return      $nodeData       Node data array
246          */
247         public function findNodeLocalBySessionId ($sessionId) {
248                 // Get search criteria
249                 $searchInstance = ObjectFactory::createObjectByConfiguredName('search_criteria_class');
250
251                 // Search for session id and limit it to one entry
252                 $searchInstance->addCriteria(self::DB_COLUMN_SESSION_ID, $sessionId);
253                 $searchInstance->setLimit(1);
254
255                 // Query database and get a result instance back
256                 $resultInstance = $this->doSelectByCriteria($searchInstance);
257
258                 // Return result instance
259                 return $resultInstance;
260         }
261
262         /**
263          * Registeres a node by given message data.
264          *
265          * @param       $messageData            An array of all message data
266          * @param       $handlerInstance        An instance of a Handleable class
267          * @return      void
268          */
269         public function registerNodeByMessageData (array $messageData, Handleable $handlerInstance) {
270                 // Get a data set instance
271                 $dataSetInstance = ObjectFactory::createObjectByConfiguredName('dataset_criteria_class', array(self::DB_TABLE_NODE_DHT));
272
273                 // Set primary key (session id)
274                 $dataSetInstance->setUniqueKey(self::DB_COLUMN_SESSION_ID);
275
276                 // Add all array elements
277                 $handlerInstance->addArrayToDataSet($dataSetInstance, $messageData);
278
279                 // Remove 'node_list'
280                 $dataSetInstance->unsetCriteria(self::DB_COLUMN_NODE_LIST);
281
282                 // Run the "INSERT" query
283                 $this->queryInsertDataSet($dataSetInstance);
284         }
285
286         /**
287          * Updates an existing entry in node list
288          *
289          * @param       $messageData            An array of all message data
290          * @param       $handlerInstance        An instance of a Handleable class
291          * @param       $searchInstance         An instance of LocalSearchCriteria class
292          * @return      void
293          */
294         public function updateNodeByMessageData (array $messageData, Handleable $handlerInstance, LocalSearchCriteria $searchInstance) {
295                 // Get a data set instance
296                 $dataSetInstance = ObjectFactory::createObjectByConfiguredName('dataset_criteria_class', array(self::DB_TABLE_NODE_DHT));
297
298                 // Add search instance
299                 $dataSetInstance->setSearchInstance($searchInstance);
300
301                 // Set primary key (session id)
302                 $dataSetInstance->setUniqueKey(self::DB_COLUMN_SESSION_ID);
303
304                 // Add all array elements
305                 $handlerInstance->addArrayToDataSet($dataSetInstance, $messageData);
306
307                 // Remove 'node_list'
308                 $dataSetInstance->unsetCriteria(self::DB_COLUMN_NODE_LIST);
309
310                 // Run the "UPDATE" query
311                 $this->queryUpdateDataSet($dataSetInstance);
312         }
313
314         /**
315          * Determines whether the given node data is already inserted in the DHT
316          *
317          * @param       $nodeData               An array with valid node data
318          * @return      $isRegistered   Whether the given node data is already inserted
319          */
320         public function isNodeRegistered (array $nodeData) {
321                 // Assert on array elements
322                 assert(isset($nodeData[self::DB_COLUMN_NODE_ID]));
323
324                 // Get search criteria
325                 $searchInstance = $this->prepareSearchInstance($nodeData);
326
327                 // Query database and get a result instance back
328                 $resultInstance = $this->doSelectByCriteria(
329                         // Search instance
330                         $searchInstance,
331                         // Only look for these array elements ("keys")
332                         array(
333                                 self::DB_COLUMN_NODE_ID     => TRUE,
334                                 self::DB_COLUMN_EXTERNAL_IP => TRUE,
335                                 self::DB_COLUMN_LISTEN_PORT => TRUE,
336                         )
337                 );
338
339                 // Check if there is an entry
340                 $isRegistered = $resultInstance->valid();
341
342                 // Return registration status
343                 return $isRegistered;
344         }
345
346         /**
347          * Registers a node with given data in the DHT. If the node is already
348          * registered this method shall throw an exception.
349          *
350          * @param       $nodeData       An array with valid node data
351          * @return      void
352          * @throws      NodeAlreadyRegisteredException  If the node is already registered
353          */
354         public function registerNode (array $nodeData) {
355                 // Assert on array elements
356                 assert(isset($nodeData[self::DB_COLUMN_NODE_ID]));
357
358                 // Is the node registered?
359                 if ($this->isNodeRegistered($nodeData)) {
360                         // Throw an exception
361                         throw new NodeAlreadyRegisteredException(array($this, $nodeData), self::EXCEPTION_NODE_ALREADY_REGISTERED);
362                 } // END - if
363
364                 // @TODO Unimplemented part
365                 $this->partialStub('nodeData=' . print_r($nodeData, TRUE));
366         }
367
368         /**
369          * Updates a node's entry in the DHT with given data. This will enrich or
370          * just update already exsiting data. If the node is not found this method
371          * shall throw an exception.
372          *
373          * @param       $nodeData       An array with valid node data
374          * @return      void
375          * @throws      NodeDataMissingException        If the node's data is missing
376          */
377         public function updateNode (array $nodeData) {
378                 // Assert on array elements
379                 assert(isset($nodeData[self::DB_COLUMN_NODE_ID]));
380
381                 // Is the node registered?
382                 if (!$this->isNodeRegistered($nodeData)) {
383                         // No, then throw an exception
384                         throw new NodeDataMissingException(array($this, $nodeData), self::EXCEPTION_NODE_NOT_REGISTERED);
385                 } // END - if
386
387                 // Get a search instance
388                 $searchInstance = $this->prepareSearchInstance($nodeData);
389
390                 // Get a data set instance
391                 $dataSetInstance = ObjectFactory::createObjectByConfiguredName('dataset_criteria_class', array(self::DB_TABLE_NODE_DHT));
392
393                 // Add search instance
394                 $dataSetInstance->setSearchInstance($searchInstance);
395
396                 // Set primary key (session id)
397                 $dataSetInstance->setUniqueKey(self::DB_COLUMN_SESSION_ID);
398
399                 // Get node instance
400                 $nodeInstance = Registry::getRegistry()->getInstance('node');
401
402                 // Add all array elements
403                 $nodeInstance->addArrayToDataSet($dataSetInstance, $nodeData);
404
405                 // Remove 'node_list'
406                 $dataSetInstance->unsetCriteria(self::DB_COLUMN_NODE_LIST);
407
408                 // Run the "UPDATE" query
409                 $this->queryUpdateDataSet($dataSetInstance);
410         }
411 }
412
413 // [EOF]
414 ?>