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