]> git.mxchange.org Git - hub.git/blob - application/hub/main/class_BaseHubSystem.php
Continued with hub:
[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 - 2014 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 communicator
53          */
54         private $communicatorInstance = NULL;
55
56         /**
57          * An instance of a crawler
58          */
59         private $crawlerInstance = NULL;
60
61         /**
62          * An instance of a cruncher
63          */
64         private $cruncherInstance = NULL;
65
66         /**
67          * An instance of a miner
68          */
69         private $minerInstance = NULL;
70
71         /**
72          * Listener instance
73          */
74         private $listenerInstance = NULL;
75
76         /**
77          * A network package handler instance
78          */
79         private $packageInstance = NULL;
80
81         /**
82          * A Receivable instance
83          */
84         private $receiverInstance = NULL;
85
86         /**
87          * State instance
88          */
89         private $stateInstance = NULL;
90
91         /**
92          * Listener pool instance
93          */
94         private $listenerPoolInstance = NULL;
95
96         /**
97          * Fragmenter instance
98          */
99         private $fragmenterInstance = NULL;
100
101         /**
102          * Decoder instance
103          */
104         private $decoderInstance = NULL;
105
106         /**
107          * Assembler instance
108          */
109         private $assemblerInstance = NULL;
110
111         /**
112          * Name of used protocol
113          */
114         private $protocolName = 'invalid';
115
116         /**
117          * Protected constructor
118          *
119          * @param       $className      Name of the class
120          * @return      void
121          */
122         protected function __construct ($className) {
123                 // Call parent constructor
124                 parent::__construct($className);
125         }
126
127         /**
128          * Getter for node instance
129          *
130          * @return      $nodeInstance   An instance of a node node
131          */
132         public final function getNodeInstance () {
133                 return $this->nodeInstance;
134         }
135
136         /**
137          * Setter for node instance
138          *
139          * @param       $nodeInstance   An instance of a node node
140          * @return      void
141          */
142         protected final function setNodeInstance (NodeHelper $nodeInstance) {
143                 $this->nodeInstance = $nodeInstance;
144         }
145
146         /**
147          * Getter for communicator instance
148          *
149          * @return      $communicatorInstance   An instance of a Communicator class
150          */
151         public final function getCommunicatorInstance () {
152                 return $this->communicatorInstance;
153         }
154
155         /**
156          * Setter for communicator instance
157          *
158          * @param       $communicatorInstance   An instance of a Communicator class
159          * @return      void
160          */
161         protected final function setCommunicatorInstance (Communicator $communicatorInstance) {
162                 $this->communicatorInstance = $communicatorInstance;
163         }
164
165         /**
166          * Getter for crawler instance
167          *
168          * @return      $crawlerInstance        An instance of a Crawler class
169          */
170         public final function getCrawlerInstance () {
171                 return $this->crawlerInstance;
172         }
173
174         /**
175          * Setter for crawler instance
176          *
177          * @param       $crawlerInstance        An instance of a Crawler class
178          * @return      void
179          */
180         protected final function setCrawlerInstance (Crawler $crawlerInstance) {
181                 $this->crawlerInstance = $crawlerInstance;
182         }
183
184         /**
185          * Getter for cruncher instance
186          *
187          * @return      $cruncherInstance       An instance of a cruncher cruncher
188          */
189         public final function getCruncherInstance () {
190                 return $this->cruncherInstance;
191         }
192
193         /**
194          * Setter for cruncher instance
195          *
196          * @param       $cruncherInstance       An instance of a cruncher cruncher
197          * @return      void
198          */
199         protected final function setCruncherInstance (CruncherHelper $cruncherInstance) {
200                 $this->cruncherInstance = $cruncherInstance;
201         }
202
203         /**
204          * Getter for miner instance
205          *
206          * @return      $minerInstance  An instance of a miner miner
207          */
208         public final function getMinerInstance () {
209                 return $this->minerInstance;
210         }
211
212         /**
213          * Setter for miner instance
214          *
215          * @param       $minerInstance  An instance of a miner miner
216          * @return      void
217          */
218         protected final function setMinerInstance (MinerHelper $minerInstance) {
219                 $this->minerInstance = $minerInstance;
220         }
221
222         /**
223          * Setter for listener instance
224          *
225          * @param       $listenerInstance       A Listenable instance
226          * @return      void
227          */
228         protected final function setListenerInstance (Listenable $listenerInstance) {
229                 $this->listenerInstance = $listenerInstance;
230         }
231
232         /**
233          * Getter for listener instance
234          *
235          * @return      $listenerInstance       A Listenable instance
236          */
237         protected final function getListenerInstance () {
238                 return $this->listenerInstance;
239         }
240
241         /**
242          * Setter for network package handler instance
243          *
244          * @param       $packageInstance        The network package instance we shall set
245          * @return      void
246          */
247         protected final function setPackageInstance (Deliverable $packageInstance) {
248                 $this->packageInstance = $packageInstance;
249         }
250
251         /**
252          * Getter for network package handler instance
253          *
254          * @return      $packageInstance        The network package handler instance we shall set
255          */
256         protected final function getPackageInstance () {
257                 return $this->packageInstance;
258         }
259
260         /**
261          * Setter for receiver instance
262          *
263          * @param       $receiverInstance       A Receivable instance we shall set
264          * @return      void
265          */
266         protected final function setReceiverInstance (Receivable $receiverInstance) {
267                 $this->receiverInstance = $receiverInstance;
268         }
269
270         /**
271          * Getter for receiver instance
272          *
273          * @return      $receiverInstance       A Receivable instance we shall get
274          */
275         protected final function getReceiverInstance () {
276                 return $this->receiverInstance;
277         }
278
279         /**
280          * Setter for state instance
281          *
282          * @param       $stateInstance  A Stateable instance
283          * @return      void
284          */
285         public final function setStateInstance (Stateable $stateInstance) {
286                 $this->stateInstance = $stateInstance;
287         }
288
289         /**
290          * Getter for state instance
291          *
292          * @return      $stateInstance  A Stateable instance
293          */
294         public final function getStateInstance () {
295                 return $this->stateInstance;
296         }
297
298         /**
299          * Setter for listener pool instance
300          *
301          * @param       $listenerPoolInstance   The new listener pool instance
302          * @return      void
303          */
304         protected final function setListenerPoolInstance (PoolableListener $listenerPoolInstance) {
305                 $this->listenerPoolInstance = $listenerPoolInstance;
306         }
307
308         /**
309          * Getter for listener pool instance
310          *
311          * @return      $listenerPoolInstance   Our current listener pool instance
312          */
313         public final function getListenerPoolInstance () {
314                 return $this->listenerPoolInstance;
315         }
316
317         /**
318          * Setter for fragmenter instance
319          *
320          * @param       $fragmenterInstance             A Fragmentable instance
321          * @return      void
322          */
323         protected final function setFragmenterInstance (Fragmentable $fragmenterInstance) {
324                 $this->fragmenterInstance = $fragmenterInstance;
325         }
326
327         /**
328          * Getter for fragmenter instance
329          *
330          * @return      $fragmenterInstance             A Fragmentable instance
331          */
332         protected final function getFragmenterInstance () {
333                 return $this->fragmenterInstance;
334         }
335
336         /**
337          * Setter for decoder instance
338          *
339          * @param       $decoderInstance        A Decodeable instance
340          * @return      void
341          */
342         protected final function setDecoderInstance (Decodeable $decoderInstance) {
343                 $this->decoderInstance = $decoderInstance;
344         }
345
346         /**
347          * Getter for decoder instance
348          *
349          * @return      $decoderInstance        A Decodeable instance
350          */
351         protected final function getDecoderInstance () {
352                 return $this->decoderInstance;
353         }
354
355         /**
356          * Setter for assembler instance
357          *
358          * @param       $assemblerInstance      A Decodeable instance
359          * @return      void
360          */
361         protected final function setAssemblerInstance (Assembler $assemblerInstance) {
362                 $this->assemblerInstance = $assemblerInstance;
363         }
364
365         /**
366          * Getter for assembler instance
367          *
368          * @return      $assemblerInstance      A Decodeable instance
369          */
370         protected final function getAssemblerInstance () {
371                 return $this->assemblerInstance;
372         }
373
374         /**
375          * Setter for node id
376          *
377          * @param       $nodeId         The new node id
378          * @return      void
379          */
380         protected final function setNodeId ($nodeId) {
381                 // Set it config now
382                 $this->getConfigInstance()->setConfigEntry('node_id', (string) $nodeId);
383         }
384
385         /**
386          * Getter for node id
387          *
388          * @return      $nodeId         Current node id
389          */
390         public final function getNodeId () {
391                 // Get it from config
392                 return $this->getConfigInstance()->getConfigEntry('node_id');
393         }
394
395         /**
396          * Setter for private key
397          *
398          * @param       $privateKey             The new private key
399          * @return      void
400          */
401         protected final function setPrivateKey ($privateKey) {
402                 // Set it config now
403                 $this->getConfigInstance()->setConfigEntry('private_key', (string) $privateKey);
404         }
405
406         /**
407          * Getter for private key
408          *
409          * @return      $privateKey             Current private key
410          */
411         public final function getPrivateKey () {
412                 // Get it from config
413                 return $this->getConfigInstance()->getConfigEntry('private_key');
414         }
415
416         /**
417          * Setter for private key hash
418          *
419          * @param       $privateKeyHash         The new private key hash
420          * @return      void
421          */
422         protected final function setPrivateKeyHash ($privateKeyHash) {
423                 // Set it config now
424                 $this->getConfigInstance()->setConfigEntry('private_key_hash', (string) $privateKeyHash);
425         }
426
427         /**
428          * Getter for private key hash
429          *
430          * @return      $privateKeyHash         Current private key hash
431          */
432         public final function getPrivateKeyHash () {
433                 // Get it from config
434                 return $this->getConfigInstance()->getConfigEntry('private_key_hash');
435         }
436
437         /**
438          * Setter for session id
439          *
440          * @param       $sessionId              The new session id
441          * @return      void
442          */
443         protected final function setSessionId ($sessionId) {
444                 $this->getConfigInstance()->setConfigEntry('session_id', (string) $sessionId);
445         }
446
447         /**
448          * Getter for session id
449          *
450          * @return      $sessionId              Current session id
451          */
452         public final function getSessionId () {
453                 return $this->getConfigInstance()->getConfigEntry('session_id');
454         }
455
456         /**
457          * Constructs a callable method name from given socket error code. If the
458          * method is not found, a generic one is used.
459          *
460          * @param       $errorCode              Error code from socket_last_error()
461          * @return      $handlerName    Call-back method name for the error handler
462          * @throws      UnsupportedSocketErrorHandlerException If the error handler is not implemented
463          */
464         protected function getSocketErrorHandlerFromCode ($errorCode) {
465                 // Create a name from translated error code
466                 $handlerName = 'socketError' . $this->convertToClassName($this->translateSocketErrorCodeToName($errorCode)) . 'Handler';
467
468                 // Is the call-back method there?
469                 if (!method_exists($this, $handlerName)) {
470                         // Please implement this
471                         throw new UnsupportedSocketErrorHandlerException(array($this, $handlerName, $errorCode), self::EXCEPTION_UNSUPPORTED_ERROR_HANDLER);
472                 } // END - if
473
474                 // Return it
475                 return $handlerName;
476         }
477
478         /**
479          * Handles socket error for given socket resource and peer data. This method
480          * validates $socketResource if it is a valid resource (see is_resource())
481          * but assumes valid data in array $recipientData, except that
482          * count($recipientData) is always 2.
483          *
484          * @param       $method                         Value of __METHOD__ from calling method
485          * @param       $line                           Value of __LINE__ from calling method
486          * @param       $socketResource         A valid socket resource
487          * @param       $recipientData          An array with two elements: 0=IP number, 1=port number
488          * @return      void
489          * @throws      InvalidSocketException  If $socketResource is no socket resource
490          * @throws      NoSocketErrorDetectedException  If socket_last_error() gives zero back
491          */
492         protected final function handleSocketError ($method, $line, $socketResource, array $recipientData) {
493                 // This method handles only socket resources
494                 if (!is_resource($socketResource)) {
495                         // No resource, abort here
496                         throw new InvalidSocketException(array($this, $socketResource), BaseListener::EXCEPTION_INVALID_SOCKET);
497                 } // END - if
498
499                 // Check count of array, should be two
500                 assert(count($recipientData) == 2);
501
502                 // Get error code for first validation (0 is not an error)
503                 $errorCode = socket_last_error($socketResource);
504
505                 // If the error code is zero, someone called this method without an error
506                 if ($errorCode == 0) {
507                         // No error detected (or previously cleared outside this method)
508                         throw new NoSocketErrorDetectedException(array($this, $socketResource), BaseListener::EXCEPTION_NO_SOCKET_ERROR);
509                 } // END - if
510
511                 // Get handler (method) name
512                 $handlerName = $this->getSocketErrorHandlerFromCode($errorCode);
513
514                 // Call-back the error handler method
515                 call_user_func_array(array($this, $handlerName), array($socketResource, $recipientData));
516
517                 // Finally clear the error because it has been handled
518                 socket_clear_error($socketResource);
519         }
520
521         /**
522          * Checks whether the final (last) chunk is valid
523          *
524          * @param       $chunks         An array with chunks and (hopefully) a valid final chunk
525          * @return      $isValid        Whether the final (last) chunk is valid
526          */
527         protected function isValidFinalChunk (array $chunks) {
528                 // Default is all fine
529                 $isValid = TRUE;
530
531                 // Split the (possible) EOP chunk
532                 $chunkSplits = explode(PackageFragmenter::CHUNK_DATA_HASH_SEPARATOR, $chunks[count($chunks) - 1]);
533
534                 // Make sure chunks with only 3 elements are parsed (for details see ChunkHandler)
535                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('eopChunk=' . $chunks[count($chunks) - 1] . ',chunkSplits=' . print_r($chunkSplits, TRUE));
536                 assert(count($chunkSplits) == 3);
537
538                 // Validate final chunk
539                 if (substr($chunkSplits[ChunkHandler::CHUNK_SPLITS_INDEX_RAW_DATA], 0, strlen(PackageFragmenter::END_OF_PACKAGE_IDENTIFIER)) != PackageFragmenter::END_OF_PACKAGE_IDENTIFIER) {
540                         // Not fine
541                         $isValid = FALSE;
542                 } elseif (substr_count($chunkSplits[ChunkHandler::CHUNK_SPLITS_INDEX_RAW_DATA], PackageFragmenter::CHUNK_HASH_SEPARATOR) != 1) {
543                         // CHUNK_HASH_SEPARATOR shall only be found once
544                         $isValid = FALSE;
545                 }
546
547                 // Return status
548                 return $isValid;
549         }
550
551         /**
552          * Translates socket error codes into our own internal names which can be
553          * used for call-backs.
554          *
555          * @param       $errorCode      The error code from socket_last_error() to be translated
556          * @return      $errorName      The translated name (all lower-case, with underlines)
557          */
558         public function translateSocketErrorCodeToName ($errorCode) {
559                 // Nothing bad happened by default
560                 $errorName = BaseRawDataHandler::SOCKET_CONNECTED;
561
562                 // Is the code a number, then we have to change it
563                 switch ($errorCode) {
564                         case 0: // Silently ignored, the socket is connected
565                                 break;
566
567                         case 11:  // "Resource temporary unavailable"
568                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_RESOURCE_UNAVAILABLE;
569                                 break;
570
571                         case 32:  // "Broken pipe"
572                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_BROKEN_PIPE;
573                                 break;
574
575                         case 104: // "Connection reset by peer"
576                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_CONNECTION_RESET_BY_PEER;
577                                 break;
578
579                         case 107: // "Transport end-point not connected"
580                         case 134: // On some (?) systems for 'transport end-point not connected'
581                                 // @TODO On some systems it is 134, on some 107?
582                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_TRANSPORT_ENDPOINT;
583                                 break;
584
585                         case 110: // "Connection timed out"
586                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_CONNECTION_TIMED_OUT;
587                                 break;
588
589                         case 111: // "Connection refused"
590                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_CONNECTION_REFUSED;
591                                 break;
592
593                         case 113: // "No route to host"
594                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_NO_ROUTE_TO_HOST;
595                                 break;
596
597                         case 114: // "Operation already in progress"
598                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_OPERATION_ALREADY_PROGRESS;
599                                 break;
600
601                         case 115: // "Operation now in progress"
602                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_OPERATION_IN_PROGRESS;
603                                 break;
604
605                         default: // Everything else <> 0
606                                 // Unhandled error code detected, so first debug it because we may want to handle it like the others
607                                 self::createDebugInstance(__CLASS__)->debugOutput('BASE-HUB[' . __METHOD__ . ':' . __LINE__ . '] UNKNOWN ERROR CODE = ' . $errorCode . ', MESSAGE = ' . socket_strerror($errorCode));
608
609                                 // Change it only in this class
610                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_UNKNOWN;
611                                 break;
612                 }
613
614                 // Return translated name
615                 return $errorName;
616         }
617
618         /**
619          * Shuts down a given socket resource. This method does only ease calling
620          * the right visitor.
621          *
622          * @param       $socketResource         A valid socket resource
623          * @return      void
624          */
625         public function shutdownSocket ($socketResource) {
626                 // Debug message
627                 self::createDebugInstance(__CLASS__)->debugOutput('HUB-SYSTEM: Shutting down socket resource ' . $socketResource . ' with state ' . $this->getPrintableState() . ' ...');
628
629                 // Set socket resource
630                 $this->setSocketResource($socketResource);
631
632                 // Get a visitor instance
633                 $visitorInstance = ObjectFactory::createObjectByConfiguredName('shutdown_socket_visitor_class');
634
635                 // Debug output
636                 self::createDebugInstance(__CLASS__)->debugOutput('HUB-SYSTEM:' . $this->__toString() . ': visitorInstance=' . $visitorInstance->__toString());
637
638                 // Call the visitor
639                 $this->accept($visitorInstance);
640         }
641
642         /**
643          * Half-shuts down a given socket resource. This method does only ease calling
644          * an other visitor than shutdownSocket() does.
645          *
646          * @param       $socketResource         A valid socket resource
647          * @return      void
648          */
649         public function halfShutdownSocket ($socketResource) {
650                 // Debug message
651                 self::createDebugInstance(__CLASS__)->debugOutput('HUB-SYSTEM: Half-shutting down socket resource ' . $socketResource . ' with state ' . $this->getPrintableState() . ' ...');
652
653                 // Set socket resource
654                 $this->setSocketResource($socketResource);
655
656                 // Get a visitor instance
657                 $visitorInstance = ObjectFactory::createObjectByConfiguredName('half_shutdown_socket_visitor_class');
658
659                 // Debug output
660                 self::createDebugInstance(__CLASS__)->debugOutput('HUB-SYSTEM:' . $this->__toString() . ': visitorInstance=' . $visitorInstance->__toString());
661
662                 // Call the visitor
663                 $this->accept($visitorInstance);
664         }
665
666         /**
667          * "Getter" for a printable state name
668          *
669          * @return      $stateName      Name of the node's state in a printable format
670          */
671         public final function getPrintableState () {
672                 // Default is 'null'
673                 $stateName = 'null';
674
675                 // Get the state instance
676                 $stateInstance = $this->getStateInstance();
677
678                 // Is it an instance of Stateable?
679                 if ($stateInstance instanceof Stateable) {
680                         // Then use that state name
681                         $stateName = $stateInstance->getStateName();
682                 } // END - if
683
684                 // Return result
685                 return $stateName;
686         }
687
688         /**
689          * Getter for protocol name
690          *
691          * @return      $protocolName   Name of used protocol
692          */
693         public final function getProtocolName () {
694                 return $this->protocolName;
695         }
696
697         /**
698          * Setter for protocol name
699          *
700          * @param       $protocolName   Name of used protocol
701          * @return      void
702          */
703         protected final function setProtocolName ($protocolName) {
704                 $this->protocolName = $protocolName;
705         }
706 }
707
708 // [EOF]
709 ?>