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