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