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