]> git.mxchange.org Git - hub.git/blob - application/hub/main/class_BaseHubSystem.php
Removed usage of array elements that are never transmitted.
[hub.git] / application / hub / main / class_BaseHubSystem.php
1 <?php
2 /**
3  * A general hub system class
4  *
5  * @author              Roland Haeder <webmaster@shipsimu.org>
6  * @version             0.0.0
7  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2012 Hub Developer Team
8  * @license             GNU GPL 3.0 or any newer version
9  * @link                http://www.shipsimu.org
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24 class BaseHubSystem extends BaseFrameworkSystem {
25         // Exception codes
26         const EXCEPTION_UNSUPPORTED_ERROR_HANDLER     = 0x900;
27         const EXCEPTION_CHUNK_ALREADY_ASSEMBLED       = 0x901;
28         const EXCEPTION_ANNOUNCEMENT_NOT_ACCEPTED     = 0x902;
29         const EXCEPTION_INVALID_CONNECTION_TYPE       = 0x903;
30         const EXCEPTION_ANNOUNCEMENT_NOT_ATTEMPTED    = 0x904;
31         const EXCEPTION_BASE64_ENCODING_NOT_MODULO_4  = 0x905;
32         const EXCEPTION_NODE_SESSION_ID_NOT_VERIFYING = 0x906;
33         const EXCEPTION_REQUEST_NOT_ACCEPTED          = 0x907;
34         const EXCEPTION_DHT_BOOTSTRAP_NOT_ACCEPTED    = 0x908;
35         const EXCEPTION_MULTIPLE_MESSAGE_SENT         = 0x909;
36         const EXCEPTION_DHT_BOOTSTRAP_NOT_ATTEMPTED   = 0x90a;
37
38         // Message status codes
39         const MESSAGE_STATUS_CODE_OKAY = 'OKAY';
40
41         /**
42          * Separator for all bootstrap node entries
43          */
44         const BOOTSTRAP_NODES_SEPARATOR = ';';
45
46         /**
47          * An instance of a node
48          */
49         private $nodeInstance = NULL;
50
51         /**
52          * An instance of a cruncher
53          */
54         private $cruncherInstance = NULL;
55
56         /**
57          * Listener instance
58          */
59         private $listenerInstance = NULL;
60
61         /**
62          * A network package handler instance
63          */
64         private $packageInstance = NULL;
65
66         /**
67          * A Receivable instance
68          */
69         private $receiverInstance = NULL;
70
71         /**
72          * State instance
73          */
74         private $stateInstance = NULL;
75
76         /**
77          * Listener pool instance
78          */
79         private $listenerPoolInstance = NULL;
80
81         /**
82          * Fragmenter instance
83          */
84         private $fragmenterInstance = NULL;
85
86         /**
87          * Decoder instance
88          */
89         private $decoderInstance = NULL;
90
91         /**
92          * Assembler instance
93          */
94         private $assemblerInstance = NULL;
95
96         /**
97          * Protected constructor
98          *
99          * @param       $className      Name of the class
100          * @return      void
101          */
102         protected function __construct ($className) {
103                 // Call parent constructor
104                 parent::__construct($className);
105         }
106
107         /**
108          * Getter for node instance
109          *
110          * @return      $nodeInstance   An instance of a node node
111          */
112         public final function getNodeInstance () {
113                 return $this->nodeInstance;
114         }
115
116         /**
117          * Setter for node instance
118          *
119          * @param       $nodeInstance   An instance of a node node
120          * @return      void
121          */
122         protected final function setNodeInstance (NodeHelper $nodeInstance) {
123                 $this->nodeInstance = $nodeInstance;
124         }
125
126         /**
127          * Getter for cruncher instance
128          *
129          * @return      $cruncherInstance       An instance of a cruncher cruncher
130          */
131         public final function getCruncherInstance () {
132                 return $this->cruncherInstance;
133         }
134
135         /**
136          * Setter for cruncher instance
137          *
138          * @param       $cruncherInstance       An instance of a cruncher cruncher
139          * @return      void
140          */
141         protected final function setCruncherInstance (CruncherHelper $cruncherInstance) {
142                 $this->cruncherInstance = $cruncherInstance;
143         }
144
145         /**
146          * Setter for listener instance
147          *
148          * @param       $listenerInstance       A Listenable instance
149          * @return      void
150          */
151         protected final function setListenerInstance (Listenable $listenerInstance) {
152                 $this->listenerInstance = $listenerInstance;
153         }
154
155         /**
156          * Getter for listener instance
157          *
158          * @return      $listenerInstance       A Listenable instance
159          */
160         protected final function getListenerInstance () {
161                 return $this->listenerInstance;
162         }
163
164         /**
165          * Setter for network package handler instance
166          *
167          * @param       $packageInstance        The network package instance we shall set
168          * @return      void
169          */
170         protected final function setPackageInstance (Deliverable $packageInstance) {
171                 $this->packageInstance = $packageInstance;
172         }
173
174         /**
175          * Getter for network package handler instance
176          *
177          * @return      $packageInstance        The network package handler instance we shall set
178          */
179         protected final function getPackageInstance () {
180                 return $this->packageInstance;
181         }
182
183         /**
184          * Setter for receiver instance
185          *
186          * @param       $receiverInstance       A Receivable instance we shall set
187          * @return      void
188          */
189         protected final function setReceiverInstance (Receivable $receiverInstance) {
190                 $this->receiverInstance = $receiverInstance;
191         }
192
193         /**
194          * Getter for receiver instance
195          *
196          * @return      $receiverInstance       A Receivable instance we shall get
197          */
198         protected final function getReceiverInstance () {
199                 return $this->receiverInstance;
200         }
201
202         /**
203          * Setter for state instance
204          *
205          * @param       $stateInstance  A Stateable instance
206          * @return      void
207          */
208         public final function setStateInstance (Stateable $stateInstance) {
209                 $this->stateInstance = $stateInstance;
210         }
211
212         /**
213          * Getter for state instance
214          *
215          * @return      $stateInstance  A Stateable instance
216          */
217         public final function getStateInstance () {
218                 return $this->stateInstance;
219         }
220
221         /**
222          * Setter for listener pool instance
223          *
224          * @param       $listenerPoolInstance   The new listener pool instance
225          * @return      void
226          */
227         protected final function setListenerPoolInstance (PoolableListener $listenerPoolInstance) {
228                 $this->listenerPoolInstance = $listenerPoolInstance;
229         }
230
231         /**
232          * Getter for listener pool instance
233          *
234          * @return      $listenerPoolInstance   Our current listener pool instance
235          */
236         public final function getListenerPoolInstance () {
237                 return $this->listenerPoolInstance;
238         }
239
240         /**
241          * Setter for fragmenter instance
242          *
243          * @param       $fragmenterInstance             A Fragmentable instance
244          * @return      void
245          */
246         protected final function setFragmenterInstance (Fragmentable $fragmenterInstance) {
247                 $this->fragmenterInstance = $fragmenterInstance;
248         }
249
250         /**
251          * Getter for fragmenter instance
252          *
253          * @return      $fragmenterInstance             A Fragmentable instance
254          */
255         protected final function getFragmenterInstance () {
256                 return $this->fragmenterInstance;
257         }
258
259         /**
260          * Setter for decoder instance
261          *
262          * @param       $decoderInstance        A Decodeable instance
263          * @return      void
264          */
265         protected final function setDecoderInstance (Decodeable $decoderInstance) {
266                 $this->decoderInstance = $decoderInstance;
267         }
268
269         /**
270          * Getter for decoder instance
271          *
272          * @return      $decoderInstance        A Decodeable instance
273          */
274         protected final function getDecoderInstance () {
275                 return $this->decoderInstance;
276         }
277
278         /**
279          * Setter for assembler instance
280          *
281          * @param       $assemblerInstance      A Decodeable instance
282          * @return      void
283          */
284         protected final function setAssemblerInstance (Assembler $assemblerInstance) {
285                 $this->assemblerInstance = $assemblerInstance;
286         }
287
288         /**
289          * Getter for assembler instance
290          *
291          * @return      $assemblerInstance      A Decodeable instance
292          */
293         protected final function getAssemblerInstance () {
294                 return $this->assemblerInstance;
295         }
296
297         /**
298          * Setter for node id
299          *
300          * @param       $nodeId         The new node id
301          * @return      void
302          */
303         protected final function setNodeId ($nodeId) {
304                 // Set it config now
305                 $this->getConfigInstance()->setConfigEntry('node_id', (string) $nodeId);
306         }
307
308         /**
309          * Getter for node id
310          *
311          * @return      $nodeId         Current node id
312          */
313         public final function getNodeId () {
314                 // Get it from config
315                 return $this->getConfigInstance()->getConfigEntry('node_id');
316         }
317
318         /**
319          * Setter for private key
320          *
321          * @param       $privateKey             The new private key
322          * @return      void
323          */
324         protected final function setPrivateKey ($privateKey) {
325                 // Set it config now
326                 $this->getConfigInstance()->setConfigEntry('private_key', (string) $privateKey);
327         }
328
329         /**
330          * Getter for private key
331          *
332          * @return      $privateKey             Current private key
333          */
334         public final function getPrivateKey () {
335                 // Get it from config
336                 return $this->getConfigInstance()->getConfigEntry('private_key');
337         }
338
339         /**
340          * Setter for private key hash
341          *
342          * @param       $privateKeyHash         The new private key hash
343          * @return      void
344          */
345         protected final function setPrivateKeyHash ($privateKeyHash) {
346                 // Set it config now
347                 $this->getConfigInstance()->setConfigEntry('private_key_hash', (string) $privateKeyHash);
348         }
349
350         /**
351          * Getter for private key hash
352          *
353          * @return      $privateKeyHash         Current private key hash
354          */
355         public final function getPrivateKeyHash () {
356                 // Get it from config
357                 return $this->getConfigInstance()->getConfigEntry('private_key_hash');
358         }
359
360         /**
361          * Setter for session id
362          *
363          * @param       $sessionId              The new session id
364          * @return      void
365          */
366         protected final function setSessionId ($sessionId) {
367                 $this->getConfigInstance()->setConfigEntry('session_id', (string) $sessionId);
368         }
369
370         /**
371          * Getter for session id
372          *
373          * @return      $sessionId              Current session id
374          */
375         public final function getSessionId () {
376                 return $this->getConfigInstance()->getConfigEntry('session_id');
377         }
378
379         /**
380          * Constructs a callable method name from given socket error code. If the
381          * method is not found, a generic one is used.
382          *
383          * @param       $errorCode              Error code from socket_last_error()
384          * @return      $handlerName    Call-back method name for the error handler
385          * @throws      UnsupportedSocketErrorHandlerException If the error handler is not implemented
386          */
387         protected function getSocketErrorHandlerFromCode ($errorCode) {
388                 // Create a name from translated error code
389                 $handlerName = 'socketError' . $this->convertToClassName($this->translateSocketErrorCodeToName($errorCode)) . 'Handler';
390
391                 // Is the call-back method there?
392                 if (!method_exists($this, $handlerName)) {
393                         // Please implement this
394                         throw new UnsupportedSocketErrorHandlerException(array($this, $handlerName, $errorCode), self::EXCEPTION_UNSUPPORTED_ERROR_HANDLER);
395                 } // END - if
396
397                 // Return it
398                 return $handlerName;
399         }
400
401         /**
402          * Handles socket error for given socket resource and peer data. This method
403          * validates $socketResource if it is a valid resource (see is_resource())
404          * but assumes valid data in array $recipientData, except that
405          * count($recipientData) is always 2.
406          *
407          * @param       $method                         Value of __METHOD__ from calling method
408          * @param       $line                           Value of __LINE__ from calling method
409          * @param       $socketResource         A valid socket resource
410          * @param       $recipientData          An array with two elements: 0=IP number, 1=port number
411          * @return      void
412          * @throws      InvalidSocketException  If $socketResource is no socket resource
413          * @throws      NoSocketErrorDetectedException  If socket_last_error() gives zero back
414          */
415         protected final function handleSocketError ($method, $line, $socketResource, array $recipientData) {
416                 // This method handles only socket resources
417                 if (!is_resource($socketResource)) {
418                         // No resource, abort here
419                         throw new InvalidSocketException(array($this, $socketResource), BaseListener::EXCEPTION_INVALID_SOCKET);
420                 } // END - if
421
422                 // Check count of array, should be two
423                 assert(count($recipientData) == 2);
424
425                 // Get error code for first validation (0 is not an error)
426                 $errorCode = socket_last_error($socketResource);
427
428                 // If the error code is zero, someone called this method without an error
429                 if ($errorCode == 0) {
430                         // No error detected (or previously cleared outside this method)
431                         throw new NoSocketErrorDetectedException(array($this, $socketResource), BaseListener::EXCEPTION_NO_SOCKET_ERROR);
432                 } // END - if
433
434                 // Get handler (method) name
435                 $handlerName = $this->getSocketErrorHandlerFromCode($errorCode);
436
437                 // Call-back the error handler method
438                 call_user_func_array(array($this, $handlerName), array($socketResource, $recipientData));
439
440                 // Finally clear the error because it has been handled
441                 socket_clear_error($socketResource);
442         }
443
444         /**
445          * Checks whether the final (last) chunk is valid
446          *
447          * @param       $chunks         An array with chunks and (hopefully) a valid final chunk
448          * @return      $isValid        Whether the final (last) chunk is valid
449          */
450         protected function isValidFinalChunk (array $chunks) {
451                 // Default is all fine
452                 $isValid = TRUE;
453
454                 // Split the (possible) EOP chunk
455                 $chunkSplits = explode(PackageFragmenter::CHUNK_DATA_HASH_SEPARATOR, $chunks[count($chunks) - 1]);
456
457                 // Make sure chunks with only 3 elements are parsed (for details see ChunkHandler)
458                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('eopChunk=' . $chunks[count($chunks) - 1] . ',chunkSplits=' . print_r($chunkSplits, TRUE));
459                 assert(count($chunkSplits) == 3);
460
461                 // Validate final chunk
462                 if (substr($chunkSplits[ChunkHandler::CHUNK_SPLITS_INDEX_RAW_DATA], 0, strlen(PackageFragmenter::END_OF_PACKAGE_IDENTIFIER)) != PackageFragmenter::END_OF_PACKAGE_IDENTIFIER) {
463                         // Not fine
464                         $isValid = FALSE;
465                 } elseif (substr_count($chunkSplits[ChunkHandler::CHUNK_SPLITS_INDEX_RAW_DATA], PackageFragmenter::CHUNK_HASH_SEPARATOR) != 1) {
466                         // CHUNK_HASH_SEPARATOR shall only be found once
467                         $isValid = FALSE;
468                 }
469
470                 // Return status
471                 return $isValid;
472         }
473
474         /**
475          * Translates socket error codes into our own internal names which can be
476          * used for call-backs.
477          *
478          * @param       $errorCode      The error code from socket_last_error() to be translated
479          * @return      $errorName      The translated name (all lower-case, with underlines)
480          */
481         public function translateSocketErrorCodeToName ($errorCode) {
482                 // Nothing bad happened by default
483                 $errorName = BaseRawDataHandler::SOCKET_CONNECTED;
484
485                 // Is the code a number, then we have to change it
486                 switch ($errorCode) {
487                         case 0: // Silently ignored, the socket is connected
488                                 break;
489
490                         case 11:  // "Resource temporary unavailable"
491                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_RESOURCE_UNAVAILABLE;
492                                 break;
493
494                         case 104: // "Connection reset by peer"
495                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_CONNECTION_RESET_BY_PEER;
496                                 break;
497
498                         case 107: // "Transport end-point not connected"
499                         case 134: // On some (?) systems for 'transport end-point not connected'
500                                 // @TODO On some systems it is 134, on some 107?
501                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_TRANSPORT_ENDPOINT;
502                                 break;
503
504                         case 110: // "Connection timed out"
505                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_CONNECTION_TIMED_OUT;
506                                 break;
507
508                         case 111: // "Connection refused"
509                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_CONNECTION_REFUSED;
510                                 break;
511
512                         case 113: // "No route to host"
513                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_NO_ROUTE_TO_HOST;
514                                 break;
515
516                         case 114: // "Operation already in progress"
517                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_OPERATION_ALREADY_PROGRESS;
518                                 break;
519
520                         case 115: // "Operation now in progress"
521                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_OPERATION_IN_PROGRESS;
522                                 break;
523
524                         default: // Everything else <> 0
525                                 // Unhandled error code detected, so first debug it because we may want to handle it like the others
526                                 self::createDebugInstance(__CLASS__)->debugOutput('BASE-HUB[' . __METHOD__ . ':' . __LINE__ . '] UNKNOWN ERROR CODE = ' . $errorCode . ', MESSAGE = ' . socket_strerror($errorCode));
527
528                                 // Change it only in this class
529                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_UNKNOWN;
530                                 break;
531                 }
532
533                 // Return translated name
534                 return $errorName;
535         }
536
537         /**
538          * Shuts down a given socket resource. This method does only ease calling
539          * the right visitor.
540          *
541          * @param       $socketResource         A valid socket resource
542          * @return      void
543          */
544         public function shutdownSocket ($socketResource) {
545                 // Debug message
546                 self::createDebugInstance(__CLASS__)->debugOutput('HUB-SYSTEM: Shutting down socket resource ' . $socketResource . ' with state ' . $this->getPrintableState() . ' ...');
547
548                 // Set socket resource
549                 $this->setSocketResource($socketResource);
550
551                 // Get a visitor instance
552                 $visitorInstance = ObjectFactory::createObjectByConfiguredName('shutdown_socket_visitor_class');
553
554                 // Debug output
555                 self::createDebugInstance(__CLASS__)->debugOutput('HUB-SYSTEM:' . $this->__toString() . ': visitorInstance=' . $visitorInstance->__toString());
556
557                 // Call the visitor
558                 $this->accept($visitorInstance);
559         }
560
561         /**
562          * Half-shuts down a given socket resource. This method does only ease calling
563          * an other visitor than shutdownSocket() does.
564          *
565          * @param       $socketResource         A valid socket resource
566          * @return      void
567          */
568         public function halfShutdownSocket ($socketResource) {
569                 // Debug message
570                 self::createDebugInstance(__CLASS__)->debugOutput('HUB-SYSTEM: Half-shutting down socket resource ' . $socketResource . ' with state ' . $this->getPrintableState() . ' ...');
571
572                 // Set socket resource
573                 $this->setSocketResource($socketResource);
574
575                 // Get a visitor instance
576                 $visitorInstance = ObjectFactory::createObjectByConfiguredName('half_shutdown_socket_visitor_class');
577
578                 // Debug output
579                 self::createDebugInstance(__CLASS__)->debugOutput('HUB-SYSTEM:' . $this->__toString() . ': visitorInstance=' . $visitorInstance->__toString());
580
581                 // Call the visitor
582                 $this->accept($visitorInstance);
583         }
584
585         /**
586          * "Getter" for a printable state name
587          *
588          * @return      $stateName      Name of the node's state in a printable format
589          */
590         public final function getPrintableState () {
591                 // Default is 'null'
592                 $stateName = 'null';
593
594                 // Get the state instance
595                 $stateInstance = $this->getStateInstance();
596
597                 // Is it an instance of Stateable?
598                 if ($stateInstance instanceof Stateable) {
599                         // Then use that state name
600                         $stateName = $stateInstance->getStateName();
601                 } // END - if
602
603                 // Return result
604                 return $stateName;
605         }
606 }
607
608 // [EOF]
609 ?>