]> git.mxchange.org Git - hub.git/blob - application/hub/main/nodes/class_BaseHubNode.php
Package class renamed, continued (sorry for lame description)
[hub.git] / application / hub / main / nodes / class_BaseHubNode.php
1 <?php
2 /**
3  * A general hub node class
4  *
5  * @author              Roland Haeder <webmaster@ship-simu.org>
6  * @version             0.0.0
7  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009, 2010 Hub Developer Team
8  * @license             GNU GPL 3.0 or any newer version
9  * @link                http://www.ship-simu.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 BaseHubNode extends BaseHubSystem implements Updateable {
25         // Exception constants
26         const EXCEPTION_HUB_ALREADY_ANNOUNCED = 0xe00;
27
28         /**
29          * IP/port number of bootstrapping node
30          */
31         private $bootIpPort = '';
32
33         /**
34          * Query connector instance
35          */
36         private $connectorInstance = null;
37
38         /**
39          * Listener pool instance
40          */
41         private $listenerPoolInstance = null;
42
43         /**
44          * Wether this node is anncounced (KEEP ON false!)
45          * @deprecated
46          */
47         private $hubIsAnnounced = false;
48
49         /**
50          * State instance
51          */
52         private $stateInstance = null;
53
54         /**
55          * Wether this hub is active
56          */
57         private $isActive = false;
58
59         /**
60          * List instance of all hubs
61          */
62         private $hubsInstance = false;
63
64         /**
65          * Protected constructor
66          *
67          * @param       $className      Name of the class
68          * @return      void
69          */
70         protected function __construct ($className) {
71                 // Call parent constructor
72                 parent::__construct($className);
73
74                 // Init list for connected hubs
75                 $this->hubsInstance = ObjectFactory::createObjectByConfiguredName('hub_list_class');
76
77                 // Init state which sets the state to 'init'
78                 $this->initState();
79         }
80
81         /**
82          * Initializes the node's state which sets it to 'init'
83          *
84          * @return      void
85          */
86         private function initState() {
87                 // Get the state factory and create the initial state, we don't need
88                 // the state instance here
89                 StateFactory::createStateInstanceByName('init', $this);
90         }
91
92         /**
93          * Setter for node id
94          *
95          * @param       $nodeId         Our new node id
96          * @return      void
97          */
98         private final function setNodeId ($nodeId) {
99                 // Set it config now
100                 $this->getConfigInstance()->setConfigEntry('node_id', (string) $nodeId);
101         }
102
103         /**
104          * Getter for node id
105          *
106          * @return      $nodeId         Current node id
107          */
108         private final function getNodeId () {
109                 // Get it from config
110                 return $this->getConfigInstance()->getConfigEntry('node_id');
111         }
112
113         /**
114          * Setter for listener pool instance
115          *
116          * @param       $listenerPoolInstance   Our new listener pool instance
117          * @return      void
118          */
119         private final function setListenerPoolInstance (PoolableListener $listenerPoolInstance) {
120                 $this->listenerPoolInstance = $listenerPoolInstance;
121         }
122
123         /**
124          * Getter for listener pool instance
125          *
126          * @return      $listenerPoolInstance   Our current listener pool instance
127          */
128         public final function getListenerPoolInstance () {
129                 return $this->listenerPoolInstance;
130         }
131
132         /**
133          * Setter for state instance
134          *
135          * @param       $stateInstance  Node's current state instance
136          * @return      void
137          */
138         public final function setStateInstance (Stateable $stateInstance) {
139                 $this->stateInstance = $stateInstance;
140         }
141
142         /**
143          * Getter for state instance
144          *
145          * @return      $stateInstance  Node's current state instance
146          */
147         public final function getStateInstance () {
148                 return $this->stateInstance;
149         }
150
151         /**
152          * Setter for session id
153          *
154          * @param       $sessionId              Our new session id
155          * @return      void
156          */
157         private final function setSessionId ($sessionId) {
158                 $this->getConfigInstance()->setConfigEntry('session_id', (string) $sessionId);
159         }
160
161         /**
162          * Getter for session id
163          *
164          * @return      $sessionId              Current session id
165          */
166         public final function getSessionId () {
167                 return $this->getConfigInstance()->getConfigEntry('session_id');
168         }
169
170         /**
171          * Setter for query instance
172          *
173          * @param       $connectorInstance              Our new query instance
174          * @return      void
175          */
176         private final function setQueryConnectorInstance (Connectable $connectorInstance) {
177                 $this->connectorInstance = $connectorInstance;
178         }
179
180         /**
181          * Getter for query instance
182          *
183          * @return      $connectorInstance              Our new query instance
184          */
185         public final function getQueryConnectorInstance () {
186                 return $this->connectorInstance;
187         }
188
189         /**
190          * Getter for boot IP/port combination
191          *
192          * @return      $bootIpPort             The IP/port combination of the boot node
193          */
194         protected final function getBootIpPort () {
195                 return $this->bootIpPort;
196         }
197
198         /**
199          * "Getter" for a printable state name
200          */
201         public final function getPrintableState () {
202                 // Default is 'null'
203                 $stateName = 'null';
204
205                 // Get the state instance
206                 $stateInstance = $this->getStateInstance();
207
208                 // Is it an instance of Stateable?
209                 if ($stateInstance instanceof Stateable) {
210                         // Then use that state name
211                         $stateName = $stateInstance->getStateName();
212                 } // END - if
213
214                 // Return result
215                 return $stateName;
216         }
217
218         /**
219          * Checks wether the given IP address matches one of the bootstrapping nodes
220          *
221          * @param       $remoteAddr             IP address to checkout against our bootstrapping list
222          * @return      $isFound                Wether the IP is found
223          */
224         protected function ifAddressMatchesBootstrappingNodes ($remoteAddr) {
225                 // By default nothing is found
226                 $isFound = false;
227
228                 // Run through all configured IPs
229                 foreach (explode(',', $this->getConfigInstance()->getConfigEntry('hub_bootstrap_nodes')) as $ipPort) {
230                         // Split it up in IP/port
231                         $ipPortArray = explode(':', $ipPort);
232
233                         // Does it match?
234                         if ($ipPortArray[0] == $remoteAddr) {
235                                 // Found it!
236                                 $isFound = true;
237
238                                 // Remember the port number
239                                 $this->bootIpPort = $ipPort;
240
241                                 // Output message
242                                 $this->debugOutput('BOOTSTRAP: ' . __FUNCTION__ . '[' . __LINE__ . ']: IP matches remote address ' . $ipPort . '.');
243
244                                 // Stop further searching
245                                 break;
246                         } elseif ($ipPortArray[0] == $this->getConfigInstance()->getConfigEntry('node_listen_addr')) {
247                                 // IP matches listen address. At this point we really don't care
248                                 // if we can also listen on that address!
249                                 $isFound = true;
250
251                                 // Remember the port number
252                                 $this->bootIpPort = $ipPort;
253
254                                 // Output message
255                                 $this->debugOutput('BOOTSTRAP: ' . __FUNCTION__ . '[' . __LINE__ . ']: IP matches listen address ' . $ipPort . '.');
256
257                                 // Stop further searching
258                                 break;
259                         }
260                 } // END - foreach
261
262                 // Return the result
263                 return $isFound;
264         }
265
266         /**
267          * Outputs the console teaser. This should only be executed on startup or
268          * full restarts. This method generates some space around the teaser.
269          *
270          * @return      void
271          */
272         public function outputConsoleTeaser () {
273                 // Get the app instance (for shortening our code)
274                 $app = $this->getApplicationInstance();
275
276                 // Output all lines
277                 $this->debugOutput(' ');
278                 $this->debugOutput($app->getAppName() . ' v' . $app->getAppVersion() . ' - ' . $this->getRequestInstance()->getRequestElement('mode') . ' mode active');
279                 $this->debugOutput('Copyright (c) 2007 - 2008 Roland Haeder, 2009, 2010 Hub Developer Team');
280                 $this->debugOutput(' ');
281                 $this->debugOutput('This program comes with ABSOLUTELY NO WARRANTY; for details see docs/COPYING.');
282                 $this->debugOutput('This is free software, and you are welcome to redistribute it under certain');
283                 $this->debugOutput('conditions; see docs/COPYING for details.');
284                 $this->debugOutput(' ');
285         }
286
287         /**
288          * Generic method to acquire a hub-id. On first run this generates a new one
289          * based on many pseudo-random data. On any later run, unless the id
290          * got not removed from database, it will be restored from the database.
291          *
292          * @param       $requestInstance        A Requestable class
293          * @param       $responseInstance       A Responseable class
294          * @return      void
295          */
296         public function bootstrapAcquireHubId (Requestable $requestInstance, Responseable $responseInstance) {
297                 // Get a wrapper instance
298                 $wrapperInstance = ObjectFactory::createObjectByConfiguredName('node_info_db_wrapper_class');
299
300                 // Now get a search criteria instance
301                 $searchInstance = ObjectFactory::createObjectByConfiguredName('search_criteria_class');
302
303                 // Search for the node number zero which is hard-coded the default
304                 $searchInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_NR, 1);
305                 $searchInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_TYPE, $this->getRequestInstance()->getRequestElement('mode'));
306                 $searchInstance->setLimit(1);
307
308                 // Get a result back
309                 $resultInstance = $wrapperInstance->doSelectByCriteria($searchInstance);
310
311                 // Is it valid?
312                 if ($resultInstance->next()) {
313                         // Save the result instance in this class
314                         $this->setResultInstance($resultInstance);
315
316                         // Get the node id from result and set it
317                         $this->setNodeId($this->getField(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_ID));
318
319                         // Output message
320                         $this->debugOutput('BOOTSTRAP: Re-using found node-id: ' . $this->getNodeId() . '');
321                 } else {
322                         // Get an RNG instance (Random Number Generator)
323                         $rngInstance = ObjectFactory::createObjectByConfiguredName('rng_class');
324
325                         // Generate a pseudo-random string
326                         $randomString = $rngInstance->randomString(255) . ':' . $this->getBootIpPort()  . ':' . $this->getRequestInstance()->getRequestElement('mode');
327
328                         // Get a crypto instance
329                         $cryptoInstance = ObjectFactory::createObjectByConfiguredName('crypto_class');
330
331                         // Hash and encrypt the string so we become a "node id" aka Hub-Id
332                         $this->setNodeId($cryptoInstance->hashString($cryptoInstance->encryptString($randomString)));
333
334                         // Register the node id with our wrapper
335                         $wrapperInstance->registerNodeId($this, $this->getRequestInstance());
336
337                         // Output message
338                         $this->debugOutput('BOOTSTRAP: Created new node-id: ' . $this->getNodeId() . '');
339                 }
340         }
341
342         /**
343          * Generates a session id which will be sent to the other hubs and clients
344          *
345          * @return      void
346          */
347         public function bootstrapGenerateSessionId () {
348                 // Get an RNG instance
349                 $rngInstance = ObjectFactory::createObjectByConfiguredName('rng_class');
350
351                 // Generate a pseudo-random string
352                 $randomString = $rngInstance->randomString(255) . ':' . $this->getBootIpPort() . ':' . $this->getRequestInstance()->getRequestElement('mode');
353
354                 // Get a crypto instance
355                 $cryptoInstance = ObjectFactory::createObjectByConfiguredName('crypto_class');
356
357                 // Hash and encrypt the string so we become a "node id" aka Hub-Id
358                 $this->setSessionId($cryptoInstance->hashString($cryptoInstance->encryptString($randomString)));
359
360                 // Get a wrapper instance
361                 $wrapperInstance = ObjectFactory::createObjectByConfiguredName('node_info_db_wrapper_class');
362
363                 // Register the node id with our wrapper
364                 $wrapperInstance->registerSessionId($this, $this->getRequestInstance());
365
366                 // Output message
367                 $this->debugOutput('BOOTSTRAP: Created new session-id: ' . $this->getSessionId() . '');
368
369                 // Change the state because the node has auired a hub id
370                 $this->getStateInstance()->nodeGeneratedSessionId();
371         }
372
373         /**
374          * Initializes queues which every node needs
375          *
376          * @return      void
377          */
378         protected function initGenericQueues () {
379                 // Debug message
380                 $this->debugOutput('BOOTSTRAP: Initialize queues: START');
381
382                 // Set the query connector instance
383                 $this->setQueryConnectorInstance(ObjectFactory::createObjectByConfiguredName('query_connector_class', array($this)));
384
385                 // Run a test query
386                 $this->getQueryConnectorInstance()->doTestQuery();
387
388                 // Debug message
389                 $this->debugOutput('BOOTSTRAP: Initialize queues: FINISHED');
390         }
391
392         /**
393          * Adds hub data elements to a given dataset instance
394          *
395          * @param       $criteriaInstance       An instance of a storeable criteria
396          * @param       $requestInstance        An instance of a Requestable class
397          * @return      void
398          */
399         public function addElementsToDataSet (StoreableCriteria $criteriaInstance, Requestable $requestInstance) {
400                 // Add node number and type
401                 $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_NR, 1);
402                 $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_TYPE, $requestInstance->getRequestElement('mode'));
403
404                 // Add the node id
405                 $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_ID, $this->getNodeId());
406
407                 // Add the session id if acquired
408                 if ($this->getSessionId() != '') {
409                         $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_SESSION_ID, $this->getSessionId());
410                 } // END - if
411         }
412
413         /**
414          * Updates a given field with new value
415          *
416          * @param       $fieldName              Field to update
417          * @param       $fieldValue             New value to store
418          * @return      void
419          * @throws      DatabaseUpdateSupportException  If this class does not support database updates
420          * @todo        Try to make this method more generic so we can move it in BaseFrameworkSystem
421          */
422         public function updateDatabaseField ($fieldName, $fieldValue) {
423                 // Unfinished
424                 $this->partialStub('Unfinished!');
425                 return;
426
427                 // Get a critieria instance
428                 $searchInstance = ObjectFactory::createObjectByConfiguredName('search_criteria_class');
429
430                 // Add search criteria
431                 $searchInstance->addCriteria(UserDatabaseWrapper::DB_COLUMN_USERNAME, $this->getUserName());
432                 $searchInstance->setLimit(1);
433
434                 // Now get another criteria
435                 $updateInstance = ObjectFactory::createObjectByConfiguredName('update_criteria_class');
436
437                 // Add criteria entry which we shall update
438                 $updateInstance->addCriteria($fieldName, $fieldValue);
439
440                 // Add the search criteria for searching for the right entry
441                 $updateInstance->setSearchInstance($searchInstance);
442
443                 // Set wrapper class name
444                 $updateInstance->setWrapperConfigEntry('user_db_wrapper_class');
445
446                 // Remember the update in database result
447                 $this->getResultInstance()->add2UpdateQueue($updateInstance);
448         }
449
450         /**
451          * Announces this hub to the upper (bootstrap or list) hubs. After this is
452          * successfully done the given task is unregistered from the handler.
453          *
454          * @param       $taskInstance   The task instance running this announcement
455          * @return      void
456          * @throws      HubAlreadyAnnouncedException    If this hub is already announced
457          * @todo        Change the first if() block to check for a specific state
458          */
459         public function announceSelfToUpperNodes (Taskable $taskInstance) {
460                 // Is this hub node announced?
461                 if ($this->hubIsAnnounced === true) {
462                         // Already announced!
463                         throw new HubAlreadyAnnouncedException($this, self::EXCEPTION_HUB_ALREADY_ANNOUNCED);
464                 } // END - if
465
466                 // Debug output
467                 $this->debugOutput('HUB: Self-announcement: START (taskInstance=' . $taskInstance->__toString(). ')');
468
469                 // Get a helper instance
470                 $helperInstance = ObjectFactory::createObjectByConfiguredName('hub_announcement_helper_class', array($this));
471
472                 // Load the announcement descriptor
473                 $helperInstance->loadAnnouncementDescriptor();
474
475                 // Set some dummy configuration entries, e.g. node_status
476                 $this->getConfigInstance()->setConfigEntry('node_status', $this->getStateInstance()->getStateName());
477
478                 // Compile all variables
479                 $helperInstance->getTemplateInstance()->compileConfigInVariables();
480
481                 // "Publish" the descriptor by sending it to the bootstrap/list nodes
482                 $helperInstance->publishAnnouncementDescriptor();
483
484                 // Change the state, this should be the last line except debug output
485                 $this->getStateInstance()->nodeAnnouncedToUpperHubs();
486
487                 // Debug output
488                 $this->debugOutput('HUB: Self-announcement: FINISHED');
489         }
490
491         /**
492          * Activates the hub by doing some final preparation and setting
493          * $hubIsActive to true
494          *
495          * @param       $requestInstance        A Requestable class
496          * @param       $responseInstance       A Responseable class
497          * @return      void
498          */
499         public function activateHub (Requestable $requestInstance, Responseable $responseInstance) {
500                 // Checks wether a listener is still active and shuts it down if one
501                 // is still listening.
502                 if (($this->determineIfListenerIsActive()) && ($this->getIsActive())) {
503                         // Shutdown them down before they can hurt anything
504                         $this->shutdownListenerPool();
505                 } // END - if
506
507                 // Get the controller here
508                 $controllerInstance = Registry::getRegistry()->getInstance('controller');
509
510                 // Run all filters for the hub activation
511                 $controllerInstance->executeActivationFilters($requestInstance, $responseInstance);
512
513                 // ----------------------- Last step from here ------------------------
514                 // Activate the hub. This is ALWAYS the last step in this method
515                 $this->getStateInstance()->nodeIsActivated();
516                 // ---------------------- Last step until here ------------------------
517         }
518
519         /**
520          * Initializes the listener pool (class)
521          *
522          * @return      void
523          */
524         public function initializeListenerPool () {
525                 // Debug output
526                 $this->debugOutput('HUB: Initialize listener: START');
527
528                 // Get a new pool instance
529                 $this->setListenerPoolInstance(ObjectFactory::createObjectByConfiguredName('listener_pool_class', array($this)));
530
531                 // Get an instance of the low-level listener
532                 $listenerInstance = ObjectFactory::createObjectByConfiguredName('tcp_listener_class', array($this));
533
534                 // Setup address and port
535                 $listenerInstance->setListenAddressByConfiguration('node_listen_addr');
536                 $listenerInstance->setListenPortByConfiguration('node_tcp_listen_port');
537
538                 // Initialize the listener
539                 $listenerInstance->initListener();
540
541                 // Get a decorator class
542                 $decoratorInstance = ObjectFactory::createObjectByConfiguredName('hub_tcp_listener_class', array($listenerInstance));
543
544                 // Add this listener to the pool
545                 $this->getListenerPoolInstance()->addListener($decoratorInstance);
546
547                 // Get a decorator class
548                 $decoratorInstance = ObjectFactory::createObjectByConfiguredName('client_tcp_listener_class', array($listenerInstance));
549
550                 // Add this listener to the pool
551                 $this->getListenerPoolInstance()->addListener($decoratorInstance);
552
553                 // Get an instance of the low-level listener
554                 $listenerInstance = ObjectFactory::createObjectByConfiguredName('udp_listener_class', array($this));
555
556                 // Setup address and port
557                 $listenerInstance->setListenAddressByConfiguration('node_listen_addr');
558                 $listenerInstance->setListenPortByConfiguration('node_udp_listen_port');
559
560                 // Initialize the listener
561                 $listenerInstance->initListener();
562
563                 // Get a decorator class
564                 $decoratorInstance = ObjectFactory::createObjectByConfiguredName('hub_udp_listener_class', array($listenerInstance));
565
566                 // Add this listener to the pool
567                 $this->getListenerPoolInstance()->addListener($decoratorInstance);
568
569                 // Get a decorator class
570                 $decoratorInstance = ObjectFactory::createObjectByConfiguredName('client_udp_listener_class', array($listenerInstance));
571
572                 // Add this listener to the pool
573                 $this->getListenerPoolInstance()->addListener($decoratorInstance);
574
575                 // Debug output
576                 $this->debugOutput('HUB: Initialize listener: FINISHED.');
577         }
578
579         /**
580          * Restores a previously stored node list from database
581          *
582          * @return      void
583          */
584         public function bootstrapRestoreNodeList () {
585                 // Debug output
586                 $this->debugOutput('HUB: Restore node list: START');
587
588                 // Get a wrapper instance
589                 $wrapperInstance = ObjectFactory::createObjectByConfiguredName('node_list_db_wrapper_class');
590
591                 // Now get a search criteria instance
592                 $searchInstance = ObjectFactory::createObjectByConfiguredName('search_criteria_class');
593
594                 // Search for the node number zero which is hard-coded the default
595                 // @TODO Add some criteria, e.g. if the node is active or so
596                 //$searchInstance->addCriteria(NodeListDatabaseWrapper::DB_COLUMN_NODE_NR, 1);
597                 //$searchInstance->addCriteria(NodeListDatabaseWrapper::DB_COLUMN_NODE_TYPE, $this->getRequestInstance()->getRequestElement('mode'));
598                 //$searchInstance->setLimit(1);
599
600                 // Get a result back
601                 $resultInstance = $wrapperInstance->doSelectByCriteria($searchInstance);
602
603                 // Is it valid?
604                 if ($resultInstance->next()) {
605                         $this->partialStub('Do something for restoring the list.');
606                         // Output message
607                         //$this->debugOutput('HUB: ');
608                 } else {
609                         // No previously saved node list found!
610                         $this->debugOutput('HUB: No previously saved node list found. This is fine.');
611                 }
612
613                 // Debug output
614                 $this->debugOutput('HUB: Restore node list: FINISHED.');
615         }
616
617         /**
618          * Getter for isActive attribute
619          *
620          * @return      $isActive       Wether the hub is active
621          */
622         public final function getIsActive () {
623                 return $this->isActive;
624         }
625
626         /**
627          * Enables (default) or disables isActive flag
628          *
629          * @param       $isActive       Wether the hub is active
630          * @return      void
631          */
632         public final function enableIsActive ($isActive = true) {
633                 $this->isActive = (bool) $isActive;
634         }
635 }
636
637 // [EOF]
638 ?>