]> git.mxchange.org Git - hub.git/blob - application/hub/classes/class_BaseHubSystem.php
Added code ...
[hub.git] / application / hub / classes / class_BaseHubSystem.php
1 <?php
2 /**
3  * A general hub system class
4  *
5  * @author              Roland Haeder <webmaster@shipsimu.org>
6  * @version             0.0.0
7  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2015 Hub Developer Team
8  * @license             GNU GPL 3.0 or any newer version
9  * @link                http://www.shipsimu.org
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24 class BaseHubSystem extends BaseFrameworkSystem {
25         // Exception codes
26         const EXCEPTION_UNSUPPORTED_ERROR_HANDLER     = 0x900;
27         const EXCEPTION_CHUNK_ALREADY_ASSEMBLED       = 0x901;
28         const EXCEPTION_ANNOUNCEMENT_NOT_ACCEPTED     = 0x902;
29         const EXCEPTION_INVALID_CONNECTION_TYPE       = 0x903;
30         const EXCEPTION_ANNOUNCEMENT_NOT_ATTEMPTED    = 0x904;
31         const EXCEPTION_BASE64_ENCODING_NOT_MODULO_4  = 0x905;
32         const EXCEPTION_NODE_SESSION_ID_NOT_VERIFYING = 0x906;
33         const EXCEPTION_REQUEST_NOT_ACCEPTED          = 0x907;
34         const EXCEPTION_DHT_BOOTSTRAP_NOT_ACCEPTED    = 0x908;
35         const EXCEPTION_MULTIPLE_MESSAGE_SENT         = 0x909;
36         const EXCEPTION_DHT_BOOTSTRAP_NOT_ATTEMPTED   = 0x90a;
37         const EXCEPTION_INVALID_UNL                   = 0x90b;
38         const EXCEPTION_INVALID_PRIVATE_KEY_HASH      = 0x90c;
39
40         // Message status codes
41         const MESSAGE_STATUS_CODE_OKAY = 'OKAY';
42
43         /**
44          * Separator for all bootstrap node entries
45          */
46         const BOOTSTRAP_NODES_SEPARATOR = ';';
47
48         /**
49          * An instance of a node
50          */
51         private $nodeInstance = NULL;
52
53         /**
54          * A network package handler instance
55          */
56         private $packageInstance = NULL;
57
58         /**
59          * A Receivable instance
60          */
61         private $receiverInstance = NULL;
62
63         /**
64          * Listener pool instance
65          */
66         private $listenerPoolInstance = NULL;
67
68         /**
69          * Fragmenter instance
70          */
71         private $fragmenterInstance = NULL;
72
73         /**
74          * Assembler instance
75          */
76         private $assemblerInstance = NULL;
77
78         /**
79          * Info instance
80          */
81         private $infoInstance = NULL;
82
83         /**
84          * Protected constructor
85          *
86          * @param       $className      Name of the class
87          * @return      void
88          */
89         protected function __construct ($className) {
90                 // Call parent constructor
91                 parent::__construct($className);
92         }
93
94         /**
95          * Setter for network package handler instance
96          *
97          * @param       $packageInstance        The network package instance we shall set
98          * @return      void
99          */
100         protected final function setPackageInstance (Deliverable $packageInstance) {
101                 $this->packageInstance = $packageInstance;
102         }
103
104         /**
105          * Getter for network package handler instance
106          *
107          * @return      $packageInstance        The network package handler instance we shall set
108          */
109         protected final function getPackageInstance () {
110                 return $this->packageInstance;
111         }
112
113         /**
114          * Setter for receiver instance
115          *
116          * @param       $receiverInstance       A Receivable instance we shall set
117          * @return      void
118          */
119         protected final function setReceiverInstance (Receivable $receiverInstance) {
120                 $this->receiverInstance = $receiverInstance;
121         }
122
123         /**
124          * Getter for receiver instance
125          *
126          * @return      $receiverInstance       A Receivable instance we shall get
127          */
128         protected final function getReceiverInstance () {
129                 return $this->receiverInstance;
130         }
131
132         /**
133          * Setter for listener pool instance
134          *
135          * @param       $listenerPoolInstance   The new listener pool instance
136          * @return      void
137          */
138         protected final function setListenerPoolInstance (PoolableListener $listenerPoolInstance) {
139                 $this->listenerPoolInstance = $listenerPoolInstance;
140         }
141
142         /**
143          * Getter for listener pool instance
144          *
145          * @return      $listenerPoolInstance   Our current listener pool instance
146          */
147         public final function getListenerPoolInstance () {
148                 return $this->listenerPoolInstance;
149         }
150
151         /**
152          * Setter for fragmenter instance
153          *
154          * @param       $fragmenterInstance             A Fragmentable instance
155          * @return      void
156          */
157         protected final function setFragmenterInstance (Fragmentable $fragmenterInstance) {
158                 $this->fragmenterInstance = $fragmenterInstance;
159         }
160
161         /**
162          * Getter for fragmenter instance
163          *
164          * @return      $fragmenterInstance             A Fragmentable instance
165          */
166         protected final function getFragmenterInstance () {
167                 return $this->fragmenterInstance;
168         }
169
170         /**
171          * Setter for assembler instance
172          *
173          * @param       $assemblerInstance      An instance of an Assembler class
174          * @return      void
175          */
176         protected final function setAssemblerInstance (Assembler $assemblerInstance) {
177                 $this->assemblerInstance = $assemblerInstance;
178         }
179
180         /**
181          * Getter for assembler instance
182          *
183          * @return      $assemblerInstance      An instance of an Assembler class
184          */
185         protected final function getAssemblerInstance () {
186                 return $this->assemblerInstance;
187         }
188
189         /**
190          * Setter for info instance
191          *
192          * @param       $infoInstance   A ShareableInfo instance
193          * @return      void
194          */
195         protected final function setInfoInstance (ShareableInfo $infoInstance) {
196                 $this->infoInstance = $infoInstance;
197         }
198
199         /**
200          * Getter for info instance
201          *
202          * @return      $infoInstance   An instance of a ShareableInfo class
203          */
204         public final function getInfoInstance () {
205                 return $this->infoInstance;
206         }
207
208         /**
209          * Setter for node id
210          *
211          * @param       $nodeId         The new node id
212          * @return      void
213          */
214         protected final function setNodeId ($nodeId) {
215                 // Set it config now
216                 $this->getConfigInstance()->setConfigEntry('node_id', (string) $nodeId);
217         }
218
219         /**
220          * Getter for node id
221          *
222          * @return      $nodeId         Current node id
223          */
224         public final function getNodeId () {
225                 // Get it from config
226                 return $this->getConfigInstance()->getConfigEntry('node_id');
227         }
228
229         /**
230          * Setter for private key
231          *
232          * @param       $privateKey             The new private key
233          * @return      void
234          */
235         protected final function setPrivateKey ($privateKey) {
236                 // Set it config now
237                 $this->getConfigInstance()->setConfigEntry('private_key', (string) $privateKey);
238         }
239
240         /**
241          * Getter for private key
242          *
243          * @return      $privateKey             Current private key
244          */
245         public final function getPrivateKey () {
246                 // Get it from config
247                 return $this->getConfigInstance()->getConfigEntry('private_key');
248         }
249
250         /**
251          * Setter for private key hash
252          *
253          * @param       $privateKeyHash         The new private key hash
254          * @return      void
255          */
256         protected final function setPrivateKeyHash ($privateKeyHash) {
257                 // Set it config now
258                 $this->getConfigInstance()->setConfigEntry('private_key_hash', (string) $privateKeyHash);
259         }
260
261         /**
262          * Getter for private key hash
263          *
264          * @return      $privateKeyHash         Current private key hash
265          */
266         public final function getPrivateKeyHash () {
267                 // Get it from config
268                 return $this->getConfigInstance()->getConfigEntry('private_key_hash');
269         }
270
271         /**
272          * Setter for session id
273          *
274          * @param       $sessionId      The new session id
275          * @return      void
276          */
277         protected final function setSessionId ($sessionId) {
278                 $this->getConfigInstance()->setConfigEntry('session_id', (string) $sessionId);
279         }
280
281         /**
282          * Getter for session id
283          *
284          * @return      $sessionId      Current session id
285          */
286         public final function getSessionId () {
287                 return $this->getConfigInstance()->getConfigEntry('session_id');
288         }
289
290         /**
291          * Constructs a callable method name from given socket error code. If the
292          * method is not found, a generic one is used.
293          *
294          * @param       $errorCode              Error code from socket_last_error()
295          * @return      $handlerName    Call-back method name for the error handler
296          * @throws      UnsupportedSocketErrorHandlerException If the error handler is not implemented
297          */
298         protected function getSocketErrorHandlerFromCode ($errorCode) {
299                 // Create a name from translated error code
300                 $handlerName = 'socketError' . self::convertToClassName($this->translateSocketErrorCodeToName($errorCode)) . 'Handler';
301
302                 // Is the call-back method there?
303                 if (!method_exists($this, $handlerName)) {
304                         // Please implement this
305                         throw new UnsupportedSocketErrorHandlerException(array($this, $handlerName, $errorCode), self::EXCEPTION_UNSUPPORTED_ERROR_HANDLER);
306                 } // END - if
307
308                 // Return it
309                 return $handlerName;
310         }
311
312         /**
313          * Handles socket error for given socket resource and peer data. This method
314          * validates $socketResource if it is a valid resource (see is_resource())
315          * but assumes valid data in array $recipientData, except that
316          * count($recipientData) is always 2.
317          *
318          * @param       $method                         Value of __METHOD__ from calling method
319          * @param       $line                           Value of __LINE__ from calling method
320          * @param       $socketResource         A valid socket resource
321          * @param       $unlData                        A valid UNL data array
322          * @return      void
323          * @throws      InvalidSocketException  If $socketResource is no socket resource
324          * @throws      NoSocketErrorDetectedException  If socket_last_error() gives zero back
325          */
326         protected final function handleSocketError ($method, $line, $socketResource, array $unlData) {
327                 // This method handles only socket resources
328                 if (!is_resource($socketResource)) {
329                         // No resource, abort here
330                         throw new InvalidSocketException(array($this, $socketResource), BaseListener::EXCEPTION_INVALID_SOCKET);
331                 } // END - if
332
333                 // Check UNL array
334                 //* DEBUG-DIE: */ die(__METHOD__ . ':unlData=' . print_r($unlData, TRUE));
335                 assert(isset($unlData[UniversalNodeLocator::UNL_PART_PROTOCOL]));
336                 assert(isset($unlData[UniversalNodeLocator::UNL_PART_ADDRESS]));
337                 assert(isset($unlData[UniversalNodeLocator::UNL_PART_PORT]));
338
339                 // Get error code for first validation (0 is not an error)
340                 $errorCode = socket_last_error($socketResource);
341
342                 // If the error code is zero, someone called this method without an error
343                 if ($errorCode == 0) {
344                         // No error detected (or previously cleared outside this method)
345                         throw new NoSocketErrorDetectedException(array($this, $socketResource), BaseListener::EXCEPTION_NO_SOCKET_ERROR);
346                 } // END - if
347
348                 // Get handler (method) name
349                 $handlerName = $this->getSocketErrorHandlerFromCode($errorCode);
350
351                 // Call-back the error handler method
352                 call_user_func_array(array($this, $handlerName), array($socketResource, $unlData));
353
354                 // Finally clear the error because it has been handled
355                 socket_clear_error($socketResource);
356         }
357
358         /**
359          * Translates socket error codes into our own internal names which can be
360          * used for call-backs.
361          *
362          * @param       $errorCode      The error code from socket_last_error() to be translated
363          * @return      $errorName      The translated name (all lower-case, with underlines)
364          */
365         public function translateSocketErrorCodeToName ($errorCode) {
366                 // Nothing bad happened by default
367                 $errorName = BaseRawDataHandler::SOCKET_CONNECTED;
368
369                 // Is the code a number, then we have to change it
370                 switch ($errorCode) {
371                         case 0: // Silently ignored, the socket is connected
372                                 break;
373
374                         case 11:  // "Resource temporary unavailable"
375                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_RESOURCE_UNAVAILABLE;
376                                 break;
377
378                         case 32:  // "Broken pipe"
379                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_BROKEN_PIPE;
380                                 break;
381
382                         case 104: // "Connection reset by peer"
383                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_CONNECTION_RESET_BY_PEER;
384                                 break;
385
386                         case 107: // "Transport end-point not connected"
387                         case 134: // On some (?) systems for 'transport end-point not connected'
388                                 // @TODO On some systems it is 134, on some 107?
389                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_TRANSPORT_ENDPOINT;
390                                 break;
391
392                         case 110: // "Connection timed out"
393                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_CONNECTION_TIMED_OUT;
394                                 break;
395
396                         case 111: // "Connection refused"
397                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_CONNECTION_REFUSED;
398                                 break;
399
400                         case 113: // "No route to host"
401                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_NO_ROUTE_TO_HOST;
402                                 break;
403
404                         case 114: // "Operation already in progress"
405                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_OPERATION_ALREADY_PROGRESS;
406                                 break;
407
408                         case 115: // "Operation now in progress"
409                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_OPERATION_IN_PROGRESS;
410                                 break;
411
412                         default: // Everything else <> 0
413                                 // Unhandled error code detected, so first debug it because we may want to handle it like the others
414                                 self::createDebugInstance(__CLASS__)->debugOutput('BASE-HUB[' . __METHOD__ . ':' . __LINE__ . '] UNKNOWN ERROR CODE = ' . $errorCode . ', MESSAGE = ' . socket_strerror($errorCode));
415
416                                 // Change it only in this class
417                                 $errorName = BaseRawDataHandler::SOCKET_ERROR_UNKNOWN;
418                                 break;
419                 }
420
421                 // Return translated name
422                 return $errorName;
423         }
424
425         /**
426          * Shuts down a given socket resource. This method does only ease calling
427          * the right visitor.
428          *
429          * @param       $socketResource         A valid socket resource
430          * @return      void
431          */
432         public function shutdownSocket ($socketResource) {
433                 // Debug message
434                 self::createDebugInstance(__CLASS__)->debugOutput('HUB-SYSTEM: Shutting down socket resource ' . $socketResource . ' with state ' . $this->getPrintableState() . ' ...');
435
436                 // Set socket resource
437                 $this->setSocketResource($socketResource);
438
439                 // Get a visitor instance
440                 $visitorInstance = ObjectFactory::createObjectByConfiguredName('shutdown_socket_visitor_class');
441
442                 // Debug output
443                 self::createDebugInstance(__CLASS__)->debugOutput('HUB-SYSTEM:' . $this->__toString() . ': visitorInstance=' . $visitorInstance->__toString());
444
445                 // Call the visitor
446                 $this->accept($visitorInstance);
447         }
448
449         /**
450          * Half-shuts down a given socket resource. This method does only ease calling
451          * an other visitor than shutdownSocket() does.
452          *
453          * @param       $socketResource         A valid socket resource
454          * @return      void
455          */
456         public function halfShutdownSocket ($socketResource) {
457                 // Debug message
458                 self::createDebugInstance(__CLASS__)->debugOutput('HUB-SYSTEM: Half-shutting down socket resource ' . $socketResource . ' with state ' . $this->getPrintableState() . ' ...');
459
460                 // Set socket resource
461                 $this->setSocketResource($socketResource);
462
463                 // Get a visitor instance
464                 $visitorInstance = ObjectFactory::createObjectByConfiguredName('half_shutdown_socket_visitor_class');
465
466                 // Debug output
467                 self::createDebugInstance(__CLASS__)->debugOutput('HUB-SYSTEM:' . $this->__toString() . ': visitorInstance=' . $visitorInstance->__toString());
468
469                 // Call the visitor
470                 $this->accept($visitorInstance);
471         }
472
473         /**
474          * "Getter" for a printable state name
475          *
476          * @return      $stateName      Name of the node's state in a printable format
477          */
478         public final function getPrintableState () {
479                 // Default is 'null'
480                 $stateName = 'null';
481
482                 // Get the state instance
483                 $stateInstance = $this->getStateInstance();
484
485                 // Is it an instance of Stateable?
486                 if ($stateInstance instanceof Stateable) {
487                         // Then use that state name
488                         $stateName = $stateInstance->getStateName();
489                 } // END - if
490
491                 // Return result
492                 return $stateName;
493         }
494 }
495
496 // [EOF]
497 ?>