3 * A general hub node class
5 * @author Roland Haeder <webmaster@ship-simu.org>
7 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 Hub Developer Team
8 * @license GNU GPL 3.0 or any newer version
9 * @link http://www.ship-simu.org
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.
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.
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/>.
24 class BaseHubNode extends BaseHubSystem implements Updateable {
33 private $sessionId = '';
36 * IP/port number of bootstrapping node
38 private $bootIpPort = '';
41 * Query connector instance
43 private $queryInstance = null;
46 * Listener pool instance
48 private $listenerPoolInstance = null;
51 * Wether the hub is active (true/false)
53 private $hubIsActive = false;
56 * Protected constructor
58 * @param $className Name of the class
61 protected function __construct ($className) {
62 // Call parent constructor
63 parent::__construct($className);
69 * @param $nodeId Our new node id
72 private final function setNodeId ($nodeId) {
73 $this->nodeId = (string) $nodeId;
79 * @return $nodeId Our new node id
81 private final function getNodeId () {
86 * Setter for listener pool instance
88 * @param $listenerPoolInstance Our new listener pool instance
91 private final function setListenerPoolInstance (PoolableListener $listenerPoolInstance) {
92 $this->listenerPoolInstance = $listenerPoolInstance;
96 * Getter for listener pool instance
98 * @return $listenerPoolInstance Our current listener pool instance
100 public final function getListenerPoolInstance () {
101 return $this->listenerPoolInstance;
105 * Setter for session id
107 * @param $sessionId Our new session id
110 private final function setSessionId ($sessionId) {
111 $this->sessionId = (string) $sessionId;
115 * Getter for session id
117 * @return $sessionId Our new session id
119 public final function getSessionId () {
120 return $this->sessionId;
124 * Setter for query instance
126 * @param $queryInstance Our new query instance
129 private final function setQueryInstance (Queryable $queryInstance) {
130 $this->queryInstance = $queryInstance;
134 * Getter for query instance
136 * @return $queryInstance Our new query instance
138 public final function getQueryInstance () {
139 return $this->queryInstance;
143 * Getter for boot IP/port combination
145 * @return $bootIpPort The IP/port combination of the boot node
147 protected final function getBootIpPort () {
148 return $this->bootIpPort;
152 * Checks wether the given IP address matches one of the bootstrapping nodes
154 * @param $remoteAddr IP address to checkout against our bootstrapping list
155 * @return $isFound Wether the IP is found
157 protected function ifAddressMatchesBootstrappingNodes ($remoteAddr) {
158 // By default nothing is found
161 // Run through all configured IPs
162 foreach (explode(',', $this->getConfigInstance()->getConfigEntry('hub_bootstrap_nodes')) as $ipPort) {
163 // Split it up in IP/port
164 $ipPortArray = explode(':', $ipPort);
167 if ($ipPortArray[0] == $remoteAddr) {
171 // Remember the port number
172 $this->bootIpPort = $ipPort;
175 $this->debugOutput('BOOTSTRAP: ' . __FUNCTION__ . '[' . __LINE__ . ']: IP matches remote address ' . $ipPort . '.');
177 // Stop further searching
179 } elseif ($ipPortArray[0] == $this->getConfigInstance()->getConfigEntry('node_listen_addr')) {
180 // IP matches listen address. At this point we really don't care
181 // if we can also listen on that address!
184 // Remember the port number
185 $this->bootIpPort = $ipPort;
188 $this->debugOutput('BOOTSTRAP: ' . __FUNCTION__ . '[' . __LINE__ . ']: IP matches listen address ' . $ipPort . '.');
190 // Stop further searching
200 * Outputs the console teaser. This should only be executed on startup or
201 * full restarts. This method generates some space around the teaser.
205 public function outputConsoleTeaser () {
206 // Get the app instance (for shortening our code)
207 $app = $this->getApplicationInstance();
210 $this->debugOutput(' ');
211 $this->debugOutput($app->getAppName() . ' v' . $app->getAppVersion() . ' - ' . $this->getRequestInstance()->getRequestElement('mode') . ' mode active');
212 $this->debugOutput('Copyright (c) 2007 - 2008 Roland Haeder, 2009 Hub Developer Team');
213 $this->debugOutput(' ');
214 $this->debugOutput('This program comes with ABSOLUTELY NO WARRANTY; for details see docs/COPYING.');
215 $this->debugOutput('This is free software, and you are welcome to redistribute it under certain');
216 $this->debugOutput('conditions; see docs/COPYING for details.');
217 $this->debugOutput(' ');
221 * Generic method to acquire a hub-id. On first run this generates a new one
222 * based on many pseudo-random data. On any later run, unless the id
223 * got not removed from database, it will be restored from the database.
227 public function bootstrapAcquireHubId () {
228 // Get a wrapper instance
229 $wrapperInstance = ObjectFactory::createObjectByConfiguredName('node_info_db_wrapper_class');
231 // Now get a search criteria instance
232 $searchInstance = ObjectFactory::createObjectByConfiguredName('search_criteria_class');
234 // Search for the node number zero which is hard-coded the default
235 $searchInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_NR, 1);
236 $searchInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_TYPE, $this->getRequestInstance()->getRequestElement('mode'));
237 $searchInstance->setLimit(1);
240 $resultInstance = $wrapperInstance->doSelectByCriteria($searchInstance);
243 if ($resultInstance->next()) {
244 // Save the result instance in this class
245 $this->setResultInstance($resultInstance);
247 // Get the node id from result and set it
248 $this->setNodeId($this->getField(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_ID));
251 $this->debugOutput('BOOTSTRAP: Re-using found node-id: ' . $this->getNodeId() . '');
253 // Get an RNG instance (Random Number Generator)
254 $rngInstance = ObjectFactory::createObjectByConfiguredName('rng_class');
256 // Generate a pseudo-random string
257 $randomString = $rngInstance->randomString(255) . ':' . $this->getBootIpPort() . ':' . $this->getRequestInstance()->getRequestElement('mode');
259 // Get a crypto instance
260 $cryptoInstance = ObjectFactory::createObjectByConfiguredName('crypto_class');
262 // Hash and encrypt the string so we become a "node id" aka Hub-Id
263 $this->setNodeId($cryptoInstance->hashString($cryptoInstance->encryptString($randomString)));
265 // Register the node id with our wrapper
266 $wrapperInstance->registerNodeId($this, $this->getRequestInstance());
269 $this->debugOutput('BOOTSTRAP: Created new node-id: ' . $this->getNodeId() . '');
274 * Generates a session id which will be sent to the other hubs and clients
278 public function bootstrapGenerateSessionId () {
279 // Get an RNG instance
280 $rngInstance = ObjectFactory::createObjectByConfiguredName('rng_class');
282 // Generate a pseudo-random string
283 $randomString = $rngInstance->randomString(255) . ':' . $this->getBootIpPort() . ':' . $this->getRequestInstance()->getRequestElement('mode');
285 // Get a crypto instance
286 $cryptoInstance = ObjectFactory::createObjectByConfiguredName('crypto_class');
288 // Hash and encrypt the string so we become a "node id" aka Hub-Id
289 $this->setSessionId($cryptoInstance->hashString($cryptoInstance->encryptString($randomString)));
291 // Get a wrapper instance
292 $wrapperInstance = ObjectFactory::createObjectByConfiguredName('node_info_db_wrapper_class');
294 // Register the node id with our wrapper
295 $wrapperInstance->registerSessionId($this, $this->getRequestInstance());
298 $this->debugOutput('BOOTSTRAP: Created new session-id: ' . $this->getSessionId() . '');
302 * Initializes queues which every node needs
306 protected function initGenericQueues () {
308 $this->debugOutput('BOOTSTRAP: Initialize queues: START');
310 // Set the query connector instance
311 $this->setQueryInstance(ObjectFactory::createObjectByConfiguredName('query_connector_class', array($this)));
314 $this->getQueryInstance()->doTestQuery();
317 $this->debugOutput('BOOTSTRAP: Initialize queues: FINISHED');
321 * Adds hub data elements to a given dataset instance
323 * @param $criteriaInstance An instance of a storeable criteria
324 * @param $requestInstance An instance of a Requestable class
327 public function addElementsToDataSet (StoreableCriteria $criteriaInstance, Requestable $requestInstance) {
328 // Add node number and type
329 $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_NR, 1);
330 $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_TYPE, $requestInstance->getRequestElement('mode'));
333 $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_ID, $this->getNodeId());
335 // Add the session id if acquired
336 if ($this->getSessionId() != '') {
337 $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_SESSION_ID, $this->getSessionId());
342 * Updates a given field with new value
344 * @param $fieldName Field to update
345 * @param $fieldValue New value to store
347 * @throws DatabaseUpdateSupportException If this class does not support database updates
348 * @todo Try to make this method more generic so we can move it in BaseFrameworkSystem
350 public function updateDatabaseField ($fieldName, $fieldValue) {
352 $this->partialStub('Unfinished!');
355 // Get a critieria instance
356 $searchInstance = ObjectFactory::createObjectByConfiguredName('search_criteria_class');
358 // Add search criteria
359 $searchInstance->addCriteria(UserDatabaseWrapper::DB_COLUMN_USERNAME, $this->getUserName());
360 $searchInstance->setLimit(1);
362 // Now get another criteria
363 $updateInstance = ObjectFactory::createObjectByConfiguredName('update_criteria_class');
365 // Add criteria entry which we shall update
366 $updateInstance->addCriteria($fieldName, $fieldValue);
368 // Add the search criteria for searching for the right entry
369 $updateInstance->setSearchInstance($searchInstance);
371 // Set wrapper class name
372 $updateInstance->setWrapperConfigEntry('user_db_wrapper_class');
374 // Remember the update in database result
375 $this->getResultInstance()->add2UpdateQueue($updateInstance);
379 * Getter for $hubIsActive attribute
381 * @return $hubIsActive Wether the hub is activer
383 public final function isHubActive () {
384 return $this->hubIsActive;
388 * Setter for $hubIsActive attribute
390 * @param $hubIsActive Wether the hub is activer
392 public final function enableHubIsActive ($hubIsActive = true) {
393 $this->hubIsActive = $hubIsActive;
397 * Activates the hub by doing some final preparation and setting
398 * $hubIsActive to true
400 * @param $requestInstance A Requestable class
401 * @param $responseInstance A Responseable class
404 public function activateHub (Requestable $requestInstance, Responseable $responseInstance) {
405 // Checks wether a listener is still active and shuts it down if one
406 // is still listening
407 if (($this->determineIfListenerIsActive()) && ($this->isHubActive())) {
408 // Shutdown them down before they can hurt anything
409 $this->shutdownListenerPool();
412 // Get the controller here
413 $controllerInstance = Registry::getRegistry()->getInstance('controller');
415 // Run all filters for the hub activation
416 $controllerInstance->executeHubActivationFilters($requestInstance, $responseInstance);
418 // ----------------------- Last step from here ------------------------
419 // Activate the hub. This is ALWAYS the last step in this method
420 $this->enableHubIsActive();
421 // ---------------------- Last step until here ------------------------
425 * Initializes the listener pool (class)
429 public function initializeListenerPool () {
431 $this->debugOutput('HUB: Initialize listener: START');
433 // Get a new pool instance
434 $this->setListenerPoolInstance(ObjectFactory::createObjectByConfiguredName('listener_pool_class', array($this)));
436 // Get an instance of the low-level listener
437 $listenerInstance = ObjectFactory::createObjectByConfiguredName('tcp_listener_class', array($this));
439 // Setup address and port
440 $listenerInstance->setListenAddressByConfiguration('node_listen_addr');
441 $listenerInstance->setListenPortByConfiguration('node_tcp_listen_port');
443 // Initialize the listener
444 $listenerInstance->initListener();
446 // Get a decorator class
447 $decoratorInstance = ObjectFactory::createObjectByConfiguredName('hub_tcp_listener_class', array($listenerInstance));
449 // Add this listener to the pool
450 $this->getListenerPoolInstance()->addListener($decoratorInstance);
452 // Get a decorator class
453 $decoratorInstance = ObjectFactory::createObjectByConfiguredName('client_tcp_listener_class', array($listenerInstance));
455 // Add this listener to the pool
456 $this->getListenerPoolInstance()->addListener($decoratorInstance);
458 // Get an instance of the low-level listener
459 $listenerInstance = ObjectFactory::createObjectByConfiguredName('udp_listener_class', array($this));
461 // Setup address and port
462 $listenerInstance->setListenAddressByConfiguration('node_listen_addr');
463 $listenerInstance->setListenPortByConfiguration('node_udp_listen_port');
465 // Initialize the listener
466 $listenerInstance->initListener();
468 // Get a decorator class
469 $decoratorInstance = ObjectFactory::createObjectByConfiguredName('hub_udp_listener_class', array($listenerInstance));
471 // Add this listener to the pool
472 $this->getListenerPoolInstance()->addListener($decoratorInstance);
474 // Get a decorator class
475 $decoratorInstance = ObjectFactory::createObjectByConfiguredName('client_udp_listener_class', array($listenerInstance));
477 // Add this listener to the pool
478 $this->getListenerPoolInstance()->addListener($decoratorInstance);
481 $this->debugOutput('HUB: Initialize listener: FINISHED.');
485 * Restores a previously stored node list from database
489 public function bootstrapRestoreNodeList () {
491 $this->debugOutput('HUB: Restore node list: START');
493 // Get a wrapper instance
494 $wrapperInstance = ObjectFactory::createObjectByConfiguredName('node_list_db_wrapper_class');
496 // Now get a search criteria instance
497 $searchInstance = ObjectFactory::createObjectByConfiguredName('search_criteria_class');
499 // Search for the node number zero which is hard-coded the default
500 // @TODO Add some criteria, e.g. if the node is active or so
501 //$searchInstance->addCriteria(NodeListDatabaseWrapper::DB_COLUMN_NODE_NR, 1);
502 //$searchInstance->addCriteria(NodeListDatabaseWrapper::DB_COLUMN_NODE_TYPE, $this->getRequestInstance()->getRequestElement('mode'));
503 //$searchInstance->setLimit(1);
506 $resultInstance = $wrapperInstance->doSelectByCriteria($searchInstance);
509 if ($resultInstance->next()) {
510 $this->partialStub('Do something for restoring the list.');
512 //$this->debugOutput('BOOTSTRAP: ');
516 $this->debugOutput('HUB: Restore node list: FINISHED.');