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