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 $connectorInstance = 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 $connectorInstance Our new query instance
129 private final function setQueryConnectorInstance (Connectable $connectorInstance) {
130 $this->connectorInstance = $connectorInstance;
134 * Getter for query instance
136 * @return $connectorInstance Our new query instance
138 public final function getQueryConnectorInstance () {
139 return $this->connectorInstance;
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->setQueryConnectorInstance(ObjectFactory::createObjectByConfiguredName('query_connector_class', array($this)));
314 $this->getQueryConnectorInstance()->doTestQuery();
317 $this->debugOutput('BOOTSTRAP: Initialize queues: FINISHED');
321 * Publish our hub descriptor to the upper hub(s)
324 * @todo This should be implemented in a generic way to keep things easy.
326 protected function publishHubDescriptor () {
327 $this->partialStub('Unfinished method.');
331 * Adds hub data elements to a given dataset instance
333 * @param $criteriaInstance An instance of a storeable criteria
334 * @param $requestInstance An instance of a Requestable class
337 public function addElementsToDataSet (StoreableCriteria $criteriaInstance, Requestable $requestInstance) {
338 // Add node number and type
339 $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_NR, 1);
340 $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_TYPE, $requestInstance->getRequestElement('mode'));
343 $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_NODE_ID, $this->getNodeId());
345 // Add the session id if acquired
346 if ($this->getSessionId() != '') {
347 $criteriaInstance->addCriteria(NodeInformationDatabaseWrapper::DB_COLUMN_SESSION_ID, $this->getSessionId());
352 * Updates a given field with new value
354 * @param $fieldName Field to update
355 * @param $fieldValue New value to store
357 * @throws DatabaseUpdateSupportException If this class does not support database updates
358 * @todo Try to make this method more generic so we can move it in BaseFrameworkSystem
360 public function updateDatabaseField ($fieldName, $fieldValue) {
362 $this->partialStub('Unfinished!');
365 // Get a critieria instance
366 $searchInstance = ObjectFactory::createObjectByConfiguredName('search_criteria_class');
368 // Add search criteria
369 $searchInstance->addCriteria(UserDatabaseWrapper::DB_COLUMN_USERNAME, $this->getUserName());
370 $searchInstance->setLimit(1);
372 // Now get another criteria
373 $updateInstance = ObjectFactory::createObjectByConfiguredName('update_criteria_class');
375 // Add criteria entry which we shall update
376 $updateInstance->addCriteria($fieldName, $fieldValue);
378 // Add the search criteria for searching for the right entry
379 $updateInstance->setSearchInstance($searchInstance);
381 // Set wrapper class name
382 $updateInstance->setWrapperConfigEntry('user_db_wrapper_class');
384 // Remember the update in database result
385 $this->getResultInstance()->add2UpdateQueue($updateInstance);
389 * Getter for $hubIsActive attribute
391 * @return $hubIsActive Wether the hub is activer
393 public final function isHubActive () {
394 return $this->hubIsActive;
398 * Setter for $hubIsActive attribute
400 * @param $hubIsActive Wether the hub is activer
402 public final function enableHubIsActive ($hubIsActive = true) {
403 $this->hubIsActive = $hubIsActive;
407 * Activates the hub by doing some final preparation and setting
408 * $hubIsActive to true
410 * @param $requestInstance A Requestable class
411 * @param $responseInstance A Responseable class
414 public function activateHub (Requestable $requestInstance, Responseable $responseInstance) {
415 // Checks wether a listener is still active and shuts it down if one
416 // is still listening
417 if (($this->determineIfListenerIsActive()) && ($this->isHubActive())) {
418 // Shutdown them down before they can hurt anything
419 $this->shutdownListenerPool();
422 // Get the controller here
423 $controllerInstance = Registry::getRegistry()->getInstance('controller');
425 // Run all filters for the hub activation
426 $controllerInstance->executeActivationFilters($requestInstance, $responseInstance);
428 // ----------------------- Last step from here ------------------------
429 // Activate the hub. This is ALWAYS the last step in this method
430 $this->enableHubIsActive();
431 // ---------------------- Last step until here ------------------------
435 * Initializes the listener pool (class)
439 public function initializeListenerPool () {
441 $this->debugOutput('HUB: Initialize listener: START');
443 // Get a new pool instance
444 $this->setListenerPoolInstance(ObjectFactory::createObjectByConfiguredName('listener_pool_class', array($this)));
446 // Get an instance of the low-level listener
447 $listenerInstance = ObjectFactory::createObjectByConfiguredName('tcp_listener_class', array($this));
449 // Setup address and port
450 $listenerInstance->setListenAddressByConfiguration('node_listen_addr');
451 $listenerInstance->setListenPortByConfiguration('node_tcp_listen_port');
453 // Initialize the listener
454 $listenerInstance->initListener();
456 // Get a decorator class
457 $decoratorInstance = ObjectFactory::createObjectByConfiguredName('hub_tcp_listener_class', array($listenerInstance));
459 // Add this listener to the pool
460 $this->getListenerPoolInstance()->addListener($decoratorInstance);
462 // Get a decorator class
463 $decoratorInstance = ObjectFactory::createObjectByConfiguredName('client_tcp_listener_class', array($listenerInstance));
465 // Add this listener to the pool
466 $this->getListenerPoolInstance()->addListener($decoratorInstance);
468 // Get an instance of the low-level listener
469 $listenerInstance = ObjectFactory::createObjectByConfiguredName('udp_listener_class', array($this));
471 // Setup address and port
472 $listenerInstance->setListenAddressByConfiguration('node_listen_addr');
473 $listenerInstance->setListenPortByConfiguration('node_udp_listen_port');
475 // Initialize the listener
476 $listenerInstance->initListener();
478 // Get a decorator class
479 $decoratorInstance = ObjectFactory::createObjectByConfiguredName('hub_udp_listener_class', array($listenerInstance));
481 // Add this listener to the pool
482 $this->getListenerPoolInstance()->addListener($decoratorInstance);
484 // Get a decorator class
485 $decoratorInstance = ObjectFactory::createObjectByConfiguredName('client_udp_listener_class', array($listenerInstance));
487 // Add this listener to the pool
488 $this->getListenerPoolInstance()->addListener($decoratorInstance);
491 $this->debugOutput('HUB: Initialize listener: FINISHED.');
495 * Restores a previously stored node list from database
499 public function bootstrapRestoreNodeList () {
501 $this->debugOutput('HUB: Restore node list: START');
503 // Get a wrapper instance
504 $wrapperInstance = ObjectFactory::createObjectByConfiguredName('node_list_db_wrapper_class');
506 // Now get a search criteria instance
507 $searchInstance = ObjectFactory::createObjectByConfiguredName('search_criteria_class');
509 // Search for the node number zero which is hard-coded the default
510 // @TODO Add some criteria, e.g. if the node is active or so
511 //$searchInstance->addCriteria(NodeListDatabaseWrapper::DB_COLUMN_NODE_NR, 1);
512 //$searchInstance->addCriteria(NodeListDatabaseWrapper::DB_COLUMN_NODE_TYPE, $this->getRequestInstance()->getRequestElement('mode'));
513 //$searchInstance->setLimit(1);
516 $resultInstance = $wrapperInstance->doSelectByCriteria($searchInstance);
519 if ($resultInstance->next()) {
520 $this->partialStub('Do something for restoring the list.');
522 //$this->debugOutput('HUB: ');
524 // No previously saved node list found!
525 $this->debugOutput('HUB: No previously saved node list found. This is fine.');
529 $this->debugOutput('HUB: Restore node list: FINISHED.');