]> git.mxchange.org Git - hub.git/blob - application/hub/main/nodes/class_BaseHubNode.php
39190abd2d12e6d23aba3fc8cf6315b307adc798
[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 - 2012 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         /**
26          * Node types
27          */
28         const NODE_TYPE_BOOT    = 'boot';
29         const NODE_TYPE_MASTER  = 'master';
30         const NODE_TYPE_LIST    = 'list';
31         const NODE_TYPE_REGULAR = 'regular';
32
33         // Exception constants
34         const EXCEPTION_HUB_ALREADY_ANNOUNCED = 0xe00;
35
36         // Other constants
37         const OBJECT_LIST_SEPARATOR = ',';
38
39         /**
40          * IP/port number of bootstrapping node
41          */
42         private $bootIpPort = '';
43
44         /**
45          * Query connector instance
46          */
47         private $queryConnectorInstance = NULL;
48
49         /**
50          * Queue connector instance
51          */
52         private $queueConnectorInstance = NULL;
53
54         /**
55          * Whether this node is anncounced (KEEP ON false!)
56          * @deprecated
57          */
58         private $hubIsAnnounced = false;
59
60         /**
61          * Whether this hub is active (default: false)
62          */
63         private $isActive = false;
64
65         /**
66          * Whether this node accepts announcements (default: false)
67          */
68         private $acceptAnnouncements = false;
69
70         /**
71          * Protected constructor
72          *
73          * @param       $className      Name of the class
74          * @return      void
75          */
76         protected function __construct ($className) {
77                 // Call parent constructor
78                 parent::__construct($className);
79
80                 // Init state which sets the state to 'init'
81                 $this->initState();
82         }
83
84         /**
85          * Initializes the node's state which sets it to 'init'
86          *
87          * @return      void
88          */
89         private function initState() {
90                 /*
91                  * Get the state factory and create the initial state, we don't need
92                  * the state instance here
93                  */
94                 NodeStateFactory::createNodeStateInstanceByName('init', $this);
95         }
96
97         /**
98          * Generates a private key and hashes it (for speeding up things)
99          *
100          * @param       $searchInstance         An instance of a LocalSearchCriteria class
101          * @return void
102          */
103         private function generatePrivateKeyAndHash (LocalSearchCriteria $searchInstance) {
104                 // Get an RNG instance (Random Number Generator)
105                 $rngInstance = ObjectFactory::createObjectByConfiguredName('rng_class');
106
107                 // Generate a pseudo-random string
108                 $randomString = $rngInstance->randomString(255) . ':' . $this->getBootIpPort()  . ':' . $this->getRequestInstance()->getRequestElement('mode');
109
110                 // Get a crypto instance
111                 $cryptoInstance = ObjectFactory::createObjectByConfiguredName('crypto_class');
112
113                 // Hash and encrypt the string so we become a node id (also documented as "hub id")
114                 $this->setPrivateKey($cryptoInstance->encryptString($randomString));
115                 $this->setPrivateKeyHash($cryptoInstance->hashString($this->getPrivateKey()));
116
117                 // Get a wrapper instance
118                 $wrapperInstance = ObjectFactory::createObjectByConfiguredName('node_info_db_wrapper_class');
119
120                 // Register the node id with our wrapper
121                 $wrapperInstance->registerPrivateKey($this, $this->getRequestInstance(), $searchInstance);
122
123                 // Output message
124                 self::createDebugInstance(__CLASS__)->debugOutput('BOOTSTRAP: Created new private key with hash: ' . $this->getPrivateKeyHash() . '');
125         }
126
127         /**
128          * Setter for query instance
129          *
130          * @param       $connectorInstance              Our new query instance
131          * @return      void
132          */
133         private final function setQueryConnectorInstance (Connectable $connectorInstance) {
134                 $this->queryConnectorInstance = $connectorInstance;
135         }
136
137         /**
138          * Getter for query instance
139          *
140          * @return      $connectorInstance              Our new query instance
141          */
142         public final function getQueryConnectorInstance () {
143                 return $this->queryConnectorInstance;
144         }
145
146         /**
147          * Setter for queue instance
148          *
149          * @param       $connectorInstance              Our new queue instance
150          * @return      void
151          */
152         private final function setQueueConnectorInstance (Connectable $connectorInstance) {
153                 $this->queueConnectorInstance = $connectorInstance;
154         }
155
156         /**
157          * Getter for queue instance
158          *
159          * @return      $connectorInstance              Our new queue instance
160          */
161         public final function getQueueConnectorInstance () {
162                 return $this->queueConnectorInstance;
163         }
164
165         /**
166          * Getter for boot IP/port combination
167          *
168          * @return      $bootIpPort             The IP/port combination of the boot node
169          */
170         protected final function getBootIpPort () {
171                 return $this->bootIpPort;
172         }
173
174         /**
175          * Checks whether the given IP address matches one of the bootstrapping nodes
176          *
177          * @param       $remoteAddr             IP address to checkout against our bootstrapping list
178          * @return      $isFound                Whether the IP is found
179          */
180         protected function ifAddressMatchesBootstrappingNodes ($remoteAddr) {
181                 // By default nothing is found
182                 $isFound = false;
183
184                 // Run through all configured IPs
185                 foreach (explode(BaseHubSystem::BOOTSTRAP_NODES_SEPARATOR, $this->getConfigInstance()->getConfigEntry('hub_bootstrap_nodes')) as $ipPort) {
186                         // Split it up in IP/port
187                         $ipPortArray = explode(':', $ipPort);
188
189                         // Does it match?
190                         if ($ipPortArray[0] == $remoteAddr) {
191                                 // Found it!
192                                 $isFound = true;
193
194                                 // Remember the port number
195                                 $this->bootIpPort = $ipPort;
196
197                                 // Output message
198                                 self::createDebugInstance(__CLASS__)->debugOutput('BOOTSTRAP: ' . __FUNCTION__ . '[' . __LINE__ . ']: IP matches remote address ' . $ipPort . '.');
199
200                                 // Stop further searching
201                                 break;
202                         } elseif ($ipPortArray[0] == $this->getConfigInstance()->getConfigEntry('node_listen_addr')) {
203                                 /*
204                                  * IP matches listen address. At this point we really don't care
205                                  * if we can really listen on that address
206                                  */
207                                 $isFound = true;
208
209                                 // Remember the port number
210                                 $this->bootIpPort = $ipPort;
211
212                                 // Output message
213                                 self::createDebugInstance(__CLASS__)->debugOutput('BOOTSTRAP: ' . __FUNCTION__ . '[' . __LINE__ . ']: IP matches listen address ' . $ipPort . '.');
214
215                                 // Stop further searching
216                                 break;
217                         }
218                 } // END - foreach
219
220                 // Return the result
221                 return $isFound;
222         }
223
224         /**
225          * Outputs the console teaser. This should only be executed on startup or
226          * full restarts. This method generates some space around the teaser.
227          *
228          * @return      void
229          */
230         public function outputConsoleTeaser () {
231                 // Get the app instance (for shortening our code)
232                 $app = $this->getApplicationInstance();
233
234                 // Output all lines
235                 self::createDebugInstance(__CLASS__)->debugOutput(' ');
236                 self::createDebugInstance(__CLASS__)->debugOutput($app->getAppName() . ' v' . $app->getAppVersion() . ' - ' . $this->getRequestInstance()->getRequestElement('mode') . ' mode active');
237                 self::createDebugInstance(__CLASS__)->debugOutput('Copyright (c) 2007 - 2008 Roland Haeder, 2009 - 2012 Hub Developer Team');
238                 self::createDebugInstance(__CLASS__)->debugOutput(' ');
239                 self::createDebugInstance(__CLASS__)->debugOutput('This program comes with ABSOLUTELY NO WARRANTY; for details see docs/COPYING.');
240                 self::createDebugInstance(__CLASS__)->debugOutput('This is free software, and you are welcome to redistribute it under certain');
241                 self::createDebugInstance(__CLASS__)->debugOutput('conditions; see docs/COPYING for details.');
242                 self::createDebugInstance(__CLASS__)->debugOutput(' ');
243         }
244
245         /**
246          * Generic method to acquire a hub-id. On first run this generates a new one
247          * based on many pseudo-random data. On any later run, unless the id
248          * got not removed from database, it will be restored from the database.
249          *
250          * @param       $requestInstance        A Requestable class
251          * @param       $responseInstance       A Responseable class
252          * @return      void
253          */
254         public function bootstrapAcquireNodeId (Requestable $requestInstance, Responseable $responseInstance) {
255                 // Get a wrapper instance
256                 $wrapperInstance = ObjectFactory::createObjectByConfiguredName('node_info_db_wrapper_class');
257
258                 // Now get a search criteria instance
259                 $searchInstance = ObjectFactory::createObjectByConfiguredName('search_criteria_class');
260
261                 // Search for the node number one which is hard-coded the default
262                 $searchInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_NR, 1);
263                 $searchInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_TYPE, $this->getRequestInstance()->getRequestElement('mode'));
264                 $searchInstance->setLimit(1);
265
266                 // Get a result back
267                 $resultInstance = $wrapperInstance->doSelectByCriteria($searchInstance);
268
269                 // Is it valid?
270                 if ($resultInstance->next()) {
271                         // Save the result instance in this class
272                         $this->setResultInstance($resultInstance);
273
274                         // Get the node id from result and set it
275                         $this->setNodeId($this->getField(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_ID));
276
277                         // Output message
278                         self::createDebugInstance(__CLASS__)->debugOutput('BOOTSTRAP: Re-using found node-id: ' . $this->getNodeId() . '');
279                 } else {
280                         // Get an RNG instance (Random Number Generator)
281                         $rngInstance = ObjectFactory::createObjectByConfiguredName('rng_class');
282
283                         // Generate a pseudo-random string
284                         $randomString = $rngInstance->randomString(255) . ':' . $this->getBootIpPort()  . ':' . $this->getRequestInstance()->getRequestElement('mode');
285
286                         // Get a crypto instance
287                         $cryptoInstance = ObjectFactory::createObjectByConfiguredName('crypto_class');
288
289                         // Hash and encrypt the string so we become a node id (also documented as "hub id")
290                         $this->setNodeId($cryptoInstance->hashString($cryptoInstance->encryptString($randomString)));
291
292                         // Register the node id with our wrapper
293                         $wrapperInstance->registerNodeId($this, $this->getRequestInstance());
294
295                         // Output message
296                         self::createDebugInstance(__CLASS__)->debugOutput('BOOTSTRAP: Created new node-id: ' . $this->getNodeId() . '');
297                 }
298         }
299
300         /**
301          * Generates a session id which will be sent to the other hubs and peers
302          *
303          * @return      void
304          */
305         public function bootstrapGenerateSessionId () {
306                 // Now get a search criteria instance
307                 $searchInstance = ObjectFactory::createObjectByConfiguredName('search_criteria_class');
308
309                 // Search for the node number one which is hard-coded the default
310                 $searchInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_NR, 1);
311                 $searchInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_TYPE, $this->getRequestInstance()->getRequestElement('mode'));
312                 $searchInstance->setLimit(1);
313
314                 // Get an RNG instance
315                 $rngInstance = ObjectFactory::createObjectByConfiguredName('rng_class');
316
317                 // Generate a pseudo-random string
318                 $randomString = $rngInstance->randomString(255) . ':' . $this->getBootIpPort() . ':' . $this->getRequestInstance()->getRequestElement('mode');
319
320                 // Get a crypto instance
321                 $cryptoInstance = ObjectFactory::createObjectByConfiguredName('crypto_class');
322
323                 // Hash and encrypt the string so we become a "node id" aka Hub-Id
324                 $this->setSessionId($cryptoInstance->hashString($cryptoInstance->encryptString($randomString)));
325
326                 // Get a wrapper instance
327                 $wrapperInstance = ObjectFactory::createObjectByConfiguredName('node_info_db_wrapper_class');
328
329                 // Register the node id with our wrapper
330                 $wrapperInstance->registerSessionId($this, $this->getRequestInstance(), $searchInstance);
331
332                 // Output message
333                 self::createDebugInstance(__CLASS__)->debugOutput('BOOTSTRAP: Created new session-id: ' . $this->getSessionId() . '');
334
335                 // Change the state because the node has auired a hub id
336                 $this->getStateInstance()->nodeGeneratedSessionId();
337         }
338
339         /**
340          * Generate a private key for en-/decryption
341          *
342          * @return      void
343          */
344         public function bootstrapGeneratePrivateKey () {
345                 // Get a wrapper instance
346                 $wrapperInstance = ObjectFactory::createObjectByConfiguredName('node_info_db_wrapper_class');
347
348                 // Now get a search criteria instance
349                 $searchInstance = ObjectFactory::createObjectByConfiguredName('search_criteria_class');
350
351                 // Search for the node number one which is hard-coded the default
352                 $searchInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_NR, 1);
353                 $searchInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_TYPE, $this->getRequestInstance()->getRequestElement('mode'));
354                 $searchInstance->setLimit(1);
355
356                 // Get a result back
357                 $resultInstance = $wrapperInstance->doSelectByCriteria($searchInstance);
358
359                 // Is it valid?
360                 if ($resultInstance->next()) {
361                         // Save the result instance in this class
362                         $this->setResultInstance($resultInstance);
363
364                         // Is the element set?
365                         if (is_null($this->getField(NodeInformationDatabaseWrapper::DB_COLUMN_PRIVATE_KEY))) {
366                                 /*
367                                  * Auto-generate the private key for e.g. out-dated database
368                                  * "tables". This allows a smooth update for the underlaying
369                                  * database table.
370                                  */
371                                 $this->generatePrivateKeyAndHash($searchInstance);
372                         } else {
373                                 // Get the node id from result and set it
374                                 $this->setPrivateKey(base64_decode($this->getField(NodeInformationDatabaseWrapper::DB_COLUMN_PRIVATE_KEY)));
375                                 $this->setPrivateKeyHash($this->getField(NodeInformationDatabaseWrapper::DB_COLUMN_PRIVATE_KEY_HASH));
376
377                                 // Output message
378                                 self::createDebugInstance(__CLASS__)->debugOutput('BOOTSTRAP: Re-using found private key hash: ' . $this->getPrivateKeyHash() . '');
379                         }
380                 } else {
381                         /*
382                          * Generate it in a private method (no confusion with 'private
383                          * method access' and 'private key' here! ;-)).
384                          */
385                         $this->generatePrivateKeyAndHash($searchInstance);
386                 }
387         }
388
389         /**
390          * Initializes queues which every node needs
391          *
392          * @return      void
393          */
394         protected function initGenericQueues () {
395                 // Debug message
396                 self::createDebugInstance(__CLASS__)->debugOutput('BOOTSTRAP: Initialize queues: START');
397
398                 // Set the query connector instance
399                 $this->setQueryConnectorInstance(ObjectFactory::createObjectByConfiguredName('query_connector_class', array($this)));
400
401                 // Run a test query
402                 $this->getQueryConnectorInstance()->doTestQuery();
403
404                 // Set the queue connector instance
405                 $this->setQueueConnectorInstance(ObjectFactory::createObjectByConfiguredName('queue_connector_class', array($this)));
406
407                 // Run a test queue
408                 $this->getQueueConnectorInstance()->doTestQueue();
409
410                 // Debug message
411                 self::createDebugInstance(__CLASS__)->debugOutput('BOOTSTRAP: Initialize queues: FINISHED');
412         }
413
414         /**
415          * Adds hub data elements to a given dataset instance
416          *
417          * @param       $criteriaInstance       An instance of a storeable criteria
418          * @param       $requestInstance        An instance of a Requestable class
419          * @return      void
420          */
421         public function addElementsToDataSet (StoreableCriteria $criteriaInstance, Requestable $requestInstance) {
422                 // Add node number and type
423                 $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_NR, 1);
424                 $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_TYPE, $requestInstance->getRequestElement('mode'));
425
426                 // Add the node id
427                 $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_ID, $this->getNodeId());
428
429                 // Add the session id if acquired
430                 if ($this->getSessionId() != '') {
431                         $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_SESSION_ID, $this->getSessionId());
432                 } // END - if
433
434                 // Add the private key if acquired
435                 if ($this->getPrivateKey() != '') {
436                         $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_PRIVATE_KEY, base64_encode($this->getPrivateKey()));
437                         $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_PRIVATE_KEY_HASH, $this->getPrivateKeyHash());
438                 } // END - if
439         }
440
441         /**
442          * Updates a given field with new value
443          *
444          * @param       $fieldName              Field to update
445          * @param       $fieldValue             New value to store
446          * @return      void
447          * @throws      DatabaseUpdateSupportException  If this class does not support database updates
448          * @todo        Try to make this method more generic so we can move it in BaseFrameworkSystem
449          */
450         public function updateDatabaseField ($fieldName, $fieldValue) {
451                 // Unfinished
452                 $this->partialStub('Unfinished!');
453                 return;
454
455                 // Get a critieria instance
456                 $searchInstance = ObjectFactory::createObjectByConfiguredName('search_criteria_class');
457
458                 // Add search criteria
459                 $searchInstance->addCriteria(UserDatabaseWrapper::DB_COLUMN_USERNAME, $this->getUserName());
460                 $searchInstance->setLimit(1);
461
462                 // Now get another criteria
463                 $updateInstance = ObjectFactory::createObjectByConfiguredName('update_criteria_class');
464
465                 // Add criteria entry which we shall update
466                 $updateInstance->addCriteria($fieldName, $fieldValue);
467
468                 // Add the search criteria for searching for the right entry
469                 $updateInstance->setSearchInstance($searchInstance);
470
471                 // Set wrapper class name
472                 $updateInstance->setWrapperConfigEntry('user_db_wrapper_class');
473
474                 // Remember the update in database result
475                 $this->getResultInstance()->add2UpdateQueue($updateInstance);
476         }
477
478         /**
479          * Announces this hub to the upper (bootstrap or list) hubs. After this is
480          * successfully done the given task is unregistered from the handler. This
481          * might look a bit overloaded here but the announcement phase isn't a
482          * simple "Hello there" message, it may later on also contain more
483          * informations like the object list.
484          *
485          * @param       $taskInstance   The task instance running this announcement
486          * @return      void
487          * @throws      NodeAlreadyAnnouncedException   If this hub is already announced
488          * @todo        Change the first if() block to check for a specific state
489          */
490         public function announceSelfToUpperNodes (Taskable $taskInstance) {
491                 // Is this hub node announced?
492                 if ($this->hubIsAnnounced === true) {
493                         // Already announced!
494                         throw new NodeAlreadyAnnouncedException($this, self::EXCEPTION_HUB_ALREADY_ANNOUNCED);
495                 } // END - if
496
497                 // Debug output
498                 self::createDebugInstance(__CLASS__)->debugOutput('HUB-Announcement: START (taskInstance=' . $taskInstance->__toString(). ')');
499
500                 // Get a helper instance
501                 $helperInstance = ObjectFactory::createObjectByConfiguredName('hub_announcement_helper_class');
502
503                 // Load the announcement descriptor
504                 $helperInstance->loadDescriptorXml();
505
506                 // Compile all variables
507                 $helperInstance->getTemplateInstance()->compileConfigInVariables();
508
509                 // "Publish" the descriptor by sending it to the bootstrap/list nodes
510                 $helperInstance->sendPackage($this);
511
512                 // Change the state, this should be the last line except debug output
513                 $this->getStateInstance()->nodeAnnouncedToUpperHubs();
514
515                 // Debug output
516                 self::createDebugInstance(__CLASS__)->debugOutput('HUB-Announcement: FINISHED');
517         }
518
519         /**
520          * Does a self-connect attempt on the public IP address. This should make
521          * it sure, we are reachable from outside world. For this kind of package we
522          * don't need that overload we have in the announcement phase.
523          *
524          * @param       $taskInstance   The task instance running this announcement
525          * @return      void
526          */
527         public function doSelfConnection (Taskable $taskInstance) {
528                 // Debug output
529                 self::createDebugInstance(__CLASS__)->debugOutput('HUB: Self Connection: START (taskInstance=' . $taskInstance->__toString(). ')');
530
531                 // Get a helper instance
532                 $helperInstance = ObjectFactory::createObjectByConfiguredName('hub_self_connect_helper_class', array($this));
533
534                 // Load the descriptor (XML) file
535                 $helperInstance->loadDescriptorXml();
536
537                 // Compile all variables
538                 $helperInstance->getTemplateInstance()->compileConfigInVariables();
539
540                 // And send the package away
541                 $helperInstance->sendPackage($this);
542
543                 // Debug output
544                 self::createDebugInstance(__CLASS__)->debugOutput('HUB: Self Connection: FINISHED');
545         }
546
547         /**
548          * Activates the hub by doing some final preparation and setting
549          * $hubIsActive to true
550          *
551          * @param       $requestInstance        A Requestable class
552          * @param       $responseInstance       A Responseable class
553          * @return      void
554          */
555         public function activateNode (Requestable $requestInstance, Responseable $responseInstance) {
556                 // Checks whether a listener is still active and shuts it down if one
557                 // is still listening.
558                 if (($this->determineIfListenerIsActive()) && ($this->isNodeActive())) {
559                         // Shutdown them down before they can hurt anything
560                         $this->shutdownListenerPool();
561                 } // END - if
562
563                 // Get the controller here
564                 $controllerInstance = Registry::getRegistry()->getInstance('controller');
565
566                 // Run all filters for the hub activation
567                 $controllerInstance->executeActivationFilters($requestInstance, $responseInstance);
568
569                 // ----------------------- Last step from here ------------------------
570                 // Activate the hub. This is ALWAYS the last step in this method
571                 $this->getStateInstance()->nodeIsActivated();
572                 // ---------------------- Last step until here ------------------------
573         }
574
575         /**
576          * Initializes the listener pool (class)
577          *
578          * @return      void
579          */
580         public function initializeListenerPool () {
581                 // Debug output
582                 self::createDebugInstance(__CLASS__)->debugOutput('HUB: Initialize listener: START');
583
584                 // Get a new pool instance
585                 $this->setListenerPoolInstance(ObjectFactory::createObjectByConfiguredName('listener_pool_class', array($this)));
586
587                 // Get an instance of the low-level listener
588                 $listenerInstance = ObjectFactory::createObjectByConfiguredName('tcp_listener_class', array($this));
589
590                 // Setup address and port
591                 $listenerInstance->setListenAddressByConfiguration('node_listen_addr');
592
593                 /*
594                  * All nodes can now use the same configuration entry because it can be
595                  * customized in config-local.php.
596                  */
597                 $listenerInstance->setListenPortByConfiguration('node_tcp_listen_port');
598
599                 // Initialize the listener
600                 $listenerInstance->initListener();
601
602                 // Get a decorator class
603                 $decoratorInstance = ObjectFactory::createObjectByConfiguredName('hub_tcp_listener_class', array($listenerInstance));
604
605                 // Add this listener to the pool
606                 $this->getListenerPoolInstance()->addListener($decoratorInstance);
607
608                 // Get a decorator class
609                 $decoratorInstance = ObjectFactory::createObjectByConfiguredName('client_tcp_listener_class', array($listenerInstance));
610
611                 // Add this listener to the pool
612                 $this->getListenerPoolInstance()->addListener($decoratorInstance);
613
614                 // Get an instance of the low-level listener
615                 $listenerInstance = ObjectFactory::createObjectByConfiguredName('udp_listener_class', array($this));
616
617                 // Setup address and port
618                 $listenerInstance->setListenAddressByConfiguration('node_listen_addr');
619
620                 /*
621                  * All nodes can now use the same configuration entry because it can be
622                  * customized in config-local.php.
623                  */
624                 $listenerInstance->setListenPortByConfiguration('node_udp_listen_port');
625
626                 // Initialize the listener
627                 $listenerInstance->initListener();
628
629                 // Get a decorator class
630                 $decoratorInstance = ObjectFactory::createObjectByConfiguredName('hub_udp_listener_class', array($listenerInstance));
631
632                 // Add this listener to the pool
633                 $this->getListenerPoolInstance()->addListener($decoratorInstance);
634
635                 // Get a decorator class
636                 $decoratorInstance = ObjectFactory::createObjectByConfiguredName('client_udp_listener_class', array($listenerInstance));
637
638                 // Add this listener to the pool
639                 $this->getListenerPoolInstance()->addListener($decoratorInstance);
640
641                 // Debug output
642                 self::createDebugInstance(__CLASS__)->debugOutput('HUB: Initialize listener: FINISHED.');
643         }
644
645         /**
646          * Restores a previously stored node list from database
647          *
648          * @return      void
649          */
650         public function bootstrapRestoreNodeList () {
651                 // Debug output
652                 self::createDebugInstance(__CLASS__)->debugOutput('HUB: Restore node list: START');
653
654                 // Get a wrapper instance
655                 $wrapperInstance = ObjectFactory::createObjectByConfiguredName('node_list_db_wrapper_class');
656
657                 // Now get a search criteria instance
658                 $searchInstance = ObjectFactory::createObjectByConfiguredName('search_criteria_class');
659
660                 // Search for the node number zero which is hard-coded the default
661                 // @TODO Add some criteria, e.g. if the node is active or so
662                 //$searchInstance->addCriteria(NodeListDatabaseWrapper::DB_COLUMN_NODE_NR, 1);
663                 //$searchInstance->addCriteria(NodeListDatabaseWrapper::DB_COLUMN_NODE_TYPE, $this->getRequestInstance()->getRequestElement('mode'));
664                 //$searchInstance->setLimit(1);
665
666                 // Get a result back
667                 $resultInstance = $wrapperInstance->doSelectByCriteria($searchInstance);
668
669                 // Is it valid?
670                 if ($resultInstance->next()) {
671                         $this->partialStub('Do something for restoring the list.');
672                         // Output message
673                         //self::createDebugInstance(__CLASS__)->debugOutput('HUB: ');
674                 } else {
675                         // No previously saved node list found!
676                         self::createDebugInstance(__CLASS__)->debugOutput('HUB: No previously saved node list found. This is fine.');
677                 }
678
679                 // Debug output
680                 self::createDebugInstance(__CLASS__)->debugOutput('HUB: Restore node list: FINISHED.');
681         }
682
683         /**
684          * Getter for isActive attribute
685          *
686          * @return      $isActive       Whether the hub is active
687          */
688         public final function isNodeActive () {
689                 return $this->isActive;
690         }
691
692         /**
693          * Enables (default) or disables isActive flag
694          *
695          * @param       $isActive       Whether the hub is active
696          * @return      void
697          */
698         public final function enableIsActive ($isActive = true) {
699                 $this->isActive = (bool) $isActive;
700         }
701
702         /**
703          * Checks whether this node accepts announcements
704          *
705          * @return      $acceptAnnouncements    Whether this node accepts announcements
706          */
707         public final function isAcceptingAnnouncements () {
708                 // Check it (this node must be active and not shutdown!)
709                 $acceptAnnouncements = (($this->acceptAnnouncements === true) && ($this->isNodeActive()));
710
711                 // Return it
712                 return $acceptAnnouncements;
713         }
714
715         /**
716          * Checks whether this node has attempted to announce itself
717          *
718          * @return      $hasAnnounced   Whether this node has attempted to announce itself
719          * @todo        Add checking if this node has been announced to the sender node
720          */
721         public function ifNodeHasAnnounced () {
722                 // Simply check the state of this node
723                 $hasAnnounced = ($this->getStateInstance() instanceof NodeAnnouncedState);
724
725                 // Return it
726                 return $hasAnnounced;
727         }
728
729         /**
730          * Enables whether this node accepts announcements
731          *
732          * @param       $acceptAnnouncements    Whether this node accepts announcements (default: true)
733          * @return      void
734          */
735         protected final function enableAcceptingAnnouncements ($acceptAnnouncements = true) {
736                 $this->acceptAnnouncements = $acceptAnnouncements;
737         }
738         
739         
740         /**
741          * "Getter" for address:port combination
742          *
743          * @param       $handlerInstance        An instance of a Networkable class
744          * @return      $addressPort            A address:port combination for this node
745          */
746         public final function getAddressPort (Networkable $handlerInstance) {
747                 // Construct config entry
748                 $configEntry = 'node_' . $handlerInstance->getHandlerName() . '_listen_port';
749
750                 // Get IP and port
751                 $addressPort = $this->getConfigInstance()->detectServerAddress() . ':' . $this->getConfigInstance()->getConfigEntry($configEntry);
752
753                 // Return it
754                 return $addressPort;
755         }
756
757         /**
758          * Updates/refreshes node data (e.g. status).
759          *
760          * @return      void
761          * @todo        Find more to do here
762          */
763         public function updateNodeData () {
764                 // Set some dummy configuration entries, e.g. node_status
765                 $this->getConfigInstance()->setConfigEntry('node_status', $this->getStateInstance()->getStateName());
766         }
767
768         /**
769          * Handles message answer by given data array
770          *
771          * @param       $messageData            A valid answer message data array
772          * @param       $packageInstance        An instance of a Receivable class
773          * @return      void
774          * @todo        Handle thrown exception
775          */
776         public function handleAnswerStatusByMessageData (array $messageData, Receivable $packageInstance) {
777                 // Construct configuration entry for handling class' name
778                 $classConfigEntry = strtolower($messageData[NetworkPackage::MESSAGE_ARRAY_TYPE] . '_status_' . $messageData[XmlAnnouncementAnswerTemplateEngine::ANNOUNCEMENT_DATA_ANSWER_STATUS]) . '_handler_class';
779
780                 // Try to get a class
781                 $handlerInstance = ObjectFactory::createObjectByConfiguredName($classConfigEntry);
782
783                 // Handle it there
784                 $handlerInstance->handleAnswerMessageData($messageData, $packageInstance);
785         }
786
787         /**
788          * "Getter" for an array of all accepted object types
789          *
790          * @return      $objectList             Array of all accepted object types
791          */
792         public function getListFromAcceptedObjectTypes () {
793                 // Get registry instance
794                 $objectRegistryInstance = ObjectTypeRegistryFactory::createObjectTypeRegistryInstance();
795
796                 // Get all entries
797                 $objectList = $objectRegistryInstance->getEntries(XmlObjectRegistryTemplateEngine::OBJECT_TYPE_DATA_NAME);
798
799                 // ... and return it
800                 return $objectList;
801         }
802 }
803
804 // [EOF]
805 ?>