]> git.mxchange.org Git - hub.git/blobdiff - application/hub/main/package/assembler/class_PackageAssembler.php
Updated 'core'.
[hub.git] / application / hub / main / package / assembler / class_PackageAssembler.php
index 3c377d145716ef02002f58c29acddd7f37aed2cb..192c54fb195599b047316f13da3c5c3268edc956 100644 (file)
@@ -3,11 +3,11 @@
  * A PackageAssembler class to assemble a package content stream fragemented
  * by PackageFragmenter back to a raw package data array.
  *
- * @author             Roland Haeder <webmaster@ship-simu.org>
+ * @author             Roland Haeder <webmaster@shipsimu.org>
  * @version            0.0.0
- * @copyright  Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2012 Hub Developer Team
+ * @copyright  Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2015 Hub Developer Team
  * @license            GNU GPL 3.0 or any newer version
- * @link               http://www.ship-simu.org
+ * @link               http://www.shipsimu.org
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
-class PackageAssembler extends BaseHubSystem implements Assembler, Registerable {
+class PackageAssembler extends BaseHubSystem implements Assembler, Registerable, Visitable {
+       /**
+        * Name for stacker holding raw data of multiple messages
+        */
+       const STACKER_NAME_MULTIPLE_MESSAGE = 'multiple_message';
+
        /**
         * Pending data
         */
        private $pendingData = '';
 
+       /**
+        * Private call-back methods
+        */
+       private $callbacks = array();
+
        /**
         * Protected constructor
         *
@@ -57,6 +67,21 @@ class PackageAssembler extends BaseHubSystem implements Assembler, Registerable
                // And set it
                $assemblerInstance->setInputStreamInstance($streamInstance);
 
+               // Now get a chunk handler instance
+               $handlerInstance = ChunkHandlerFactory::createChunkHandlerInstance();
+
+               // Set handler instance
+               $assemblerInstance->setHandlerInstance($handlerInstance);
+
+               // Get stacker instance
+               $stackInstance = ObjectFactory::createObjectByConfiguredName('multiple_message_stacker_class');
+
+               // Initialize the only one stack
+               $stackInstance->initStack(self::STACKER_NAME_MULTIPLE_MESSAGE);
+
+               // And add it
+               $assemblerInstance->setStackInstance($stackInstance);
+
                // Return the prepared instance
                return $assemblerInstance;
        }
@@ -68,10 +93,10 @@ class PackageAssembler extends BaseHubSystem implements Assembler, Registerable
         */
        private function ifInputBufferIsEmpty () {
                // Check it
-               $isInputBufferEmpty = $this->getPackageInstance()->getStackerInstance()->isStackEmpty(NetworkPackage::STACKER_NAME_DECODED_HANDLED);
+               $isInputBufferEmpty = $this->getPackageInstance()->getStackInstance()->isStackEmpty(NetworkPackage::STACKER_NAME_DECODED_HANDLED);
 
                // Debug message
-               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-ASSEMBLER: isInputBufferEmpty=' . intval($isInputBufferEmpty));
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-ASSEMBLER[' . __METHOD__ . ':' . __LINE__ . ']: isInputBufferEmpty=' . intval($isInputBufferEmpty));
 
                // Return it
                return $isInputBufferEmpty;
@@ -85,7 +110,7 @@ class PackageAssembler extends BaseHubSystem implements Assembler, Registerable
         */
        private function isPackageContentCompleted (array $packageContent) {
                // Check both
-               $isCompleted = $this->ifStartEndMarkerSet($packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA]);
+               $isCompleted = $this->ifStartEndMarkersSet($packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA]);
 
                // Return status
                return $isCompleted;
@@ -113,16 +138,16 @@ class PackageAssembler extends BaseHubSystem implements Assembler, Registerable
                );
 
                // Construct call-back name from package error code
-               $methodName = 'handlePackageBy' . $this->convertToClassName($packageContent[BaseRawDataHandler::PACKAGE_ERROR_CODE]);
+               $this->callbacks[$packageContent[BaseRawDataHandler::PACKAGE_ERROR_CODE]] = 'handlePackageBy' . self::convertToClassName($packageContent[BaseRawDataHandler::PACKAGE_ERROR_CODE]);
 
                // Abort if the call-back method is not there
-               if (!method_exists($this, $methodName)) {
+               if (!method_exists($this, $this->callbacks[$packageContent[BaseRawDataHandler::PACKAGE_ERROR_CODE]])) {
                        // Throw an exception
-                       throw new UnsupportedPackageCodeHandlerException(array($this, $methodName, $packageContent), BaseListener::EXCEPTION_UNSUPPORTED_PACKAGE_CODE_HANDLER);
+                       throw new UnsupportedPackageCodeHandlerException(array($this, $this->callbacks[$packageContent[BaseRawDataHandler::PACKAGE_ERROR_CODE]], $packageContent), BaseListener::EXCEPTION_UNSUPPORTED_PACKAGE_CODE_HANDLER);
                } // END - if
 
                // Call it back
-               call_user_func(array($this, $methodName), $packageContent);
+               call_user_func(array($this, $this->callbacks[$packageContent[BaseRawDataHandler::PACKAGE_ERROR_CODE]]), $packageContent);
        }
 
        /**************************************************************************
@@ -130,18 +155,17 @@ class PackageAssembler extends BaseHubSystem implements Assembler, Registerable
         **************************************************************************/
 
        /**
-        * Call-back handler to handle unhandled packages. This method "explodes"
-        * the string with the chunk separator from PackageFragmenter class, does
-        * some low checks on it and feeds it into another queue for verification
-        * and re-request for bad chunks.
+        * Call-back handler to handle unhandled package data. This method
+        * "explodes" the string with the chunk separator from PackageFragmenter
+        * class, does some low checks on it and feeds it into another queue for
+        * verification and re-request for bad chunks.
         *
         * @param       $packageContent         An array with two elements: 'raw_data' and 'error_code'
         * @return      void
-        * @throws      FinalChunkVerificationException         If the final chunk does not start with 'EOP:'
         */
        private function handlePackageByUnhandledPackage (array $packageContent) {
                // Debug message
-               /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-ASSEMBLER: packageData[' . BaseRawDataHandler::PACKAGE_RAW_DATA . ']=' . $packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA]);
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-ASSEMBLER[' . __METHOD__ . ':' . __LINE__ . ']: packageData[' . BaseRawDataHandler::PACKAGE_RAW_DATA . ']=' . $packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA]);
 
                // Check for some conditions
                if ((!$this->ifInputBufferIsEmpty()) || (!$this->isPackageContentCompleted($packageContent))) {
@@ -149,23 +173,10 @@ class PackageAssembler extends BaseHubSystem implements Assembler, Registerable
                        $this->pendingData .= $packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA];
 
                        // Debug message
-                       /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-ASSEMBLER: Partial data received. Waiting for more ... ( ' . strlen($packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA]) . ' bytes)');
+                       //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-ASSEMBLER[' . __METHOD__ . ':' . __LINE__ . ']: Partial data received. Waiting for more ... ( ' . strlen($packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA]) . ' bytes)');
                } else {
                        // Debug message
-                       //* NOISY-DEBUG */ self::createDebugInstance(__CLASS__)->debugOutput('packageContent=' . print_r($packageContent,true) . ',chunks='.print_r($chunks,true));
-
-                       /*
-                        * "explode" the string from 'raw_data' with chunk separator to
-                        * get an array of chunks. These chunks must then be verified by
-                        * their checksums. Also the final chunk must be handled.
-                        */
-                       $chunks = explode(PackageFragmenter::CHUNK_SEPARATOR, $packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA]);
-
-                       // Now get a chunk handler instance
-                       $handlerInstance = ChunkHandlerFactory::createChunkHandlerInstance();
-
-                       // Add all chunks because the last final chunk is found
-                       $handlerInstance->addAllChunksWithFinal($chunks);
+                       //* DEBUG */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-ASSEMBLER[' . __METHOD__ . ':' . __LINE__ . ': packageContent=' . print_r($packageContent, TRUE) . ',chunks='.print_r($chunks, TRUE));
                }
        }
 
@@ -183,29 +194,127 @@ class PackageAssembler extends BaseHubSystem implements Assembler, Registerable
                return $ifPendingDataIsEmpty;
        }
 
+       /**
+        * Checks whether the assembler has multiple messages pending
+        *
+        * @return      $isPending      Whether the assembler has multiple messages pending
+        */
+       public function ifMultipleMessagesPending () {
+               // Determine it
+               $isPending = (!$this->getStackInstance()->isStackEmpty(self::STACKER_NAME_MULTIPLE_MESSAGE));
+
+               // Return it
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-ASSEMBLER[' . __METHOD__ . ':' . __LINE__ . ': isPending=' . intval($isPending));
+               return $isPending;
+       }
+
        /**
         * Handles the assembler's pending data
         *
         * @return      void
         */
        public function handlePendingData () {
+               // Debug output
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-ASSEMBLER[' . __METHOD__ . ':' . __LINE__ . ']: Going to decode ' . strlen($this->pendingData) . ' Bytes of pending data. pendingData=' . $this->pendingData);
+
                // Assert on condition
                assert(!$this->isPendingDataEmpty());
 
+               // No markers set?
+               if (!$this->ifStartEndMarkersSet($this->pendingData)) {
+                       // This will cause an assertition in next call, so simply wait for more data
+                       //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-ASSEMBLER[' . __METHOD__ . ':' . __LINE__ . ']: Pending data of ' . strlen($this->pendingData) . ' Bytes are incomplete, waiting for more ...');
+                       //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-ASSEMBLER[' . __METHOD__ . ':' . __LINE__ . ']: this->pendingData=' . $this->pendingData);
+                       return;
+               } elseif (substr_count($this->pendingData, BaseRawDataHandler::STREAM_START_MARKER) > 1) {
+                       /*
+                        * Multiple messages found, so split off first message as the input
+                        * stream can only handle one message per time.
+                        */
+                       foreach (explode(BaseRawDataHandler::STREAM_START_MARKER, $this->pendingData) as $message) {
+                               // Prepend start marker again as it is needed to decode the message.
+                               $message = BaseRawDataHandler::STREAM_START_MARKER . $message;
+
+                               // Push it on stack
+                               $this->getStackInstance()->pushNamed(self::STACKER_NAME_MULTIPLE_MESSAGE, $message);
+                       } // END - foreach
+
+                       // Clear pending data
+                       $this->clearPendingData();
+
+                       // ... and exit here
+                       return;
+               }
+
                // Init fake array
                $packageContent = array(
                        BaseRawDataHandler::PACKAGE_RAW_DATA   => $this->getInputStreamInstance()->streamData($this->pendingData),
                        BaseRawDataHandler::PACKAGE_ERROR_CODE => BaseRawDataHandler::SOCKET_ERROR_UNHANDLED
                );
 
-               // Clear pending data
-               $this->pendingData = '';
+               /*
+                * Clear pending data as it has been processed and will be handled some
+                * lines below.
+                */
+               $this->clearPendingData();
 
                // Debug message
-               /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-ASSEMBLER: Last block of partial data received. A total of ' . strlen($packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA]) . ' bytes has been received.');
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-ASSEMBLER[' . __METHOD__ . ':' . __LINE__ . ']: Last block of partial data received. A total of ' . strlen($packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA]) . ' bytes has been received.');
+
+               // Make sure last CHUNK_SEPARATOR is not there
+               if (substr($packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA], -1, 1) == PackageFragmenter::CHUNK_SEPARATOR) {
+                       // Remove it
+                       $packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA] = substr($packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA], 0, -1);
+               } // END - if
+
+               /*
+                * "explode" the string from 'raw_data' with chunk separator to get an
+                * array of chunks. These chunks must then be verified by their
+                * checksums. Also the final chunk must be handled.
+                */
+               $chunks = explode(PackageFragmenter::CHUNK_SEPARATOR, $packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA]);
+
+               // Add all chunks because the last final chunk is found
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-ASSEMBLER[' . __METHOD__ . ':' . __LINE__ . ']: Going to add ' . count($chunks) . ' to chunk handler ...');
+               $this->getHandlerInstance()->addAllChunksWithFinal($chunks);
+       }
+
+       /**
+        * Handles multiple messages.
+        *
+        * @return      void
+        */
+       public function handleMultipleMessages () {
+               // Assert on condition
+               assert($this->ifMultipleMessagesPending());
+               assert($this->isPendingDataEmpty());
 
-               // Call the real handler method
-               $this->handlePackageByUnhandledPackage($packageContent);
+               // "Pop" next entry from stack and set it as new pending data
+               $this->pendingData = $this->getStackInstance()->popNamed(self::STACKER_NAME_MULTIPLE_MESSAGE);
+
+               // And handle it
+               $this->handlePendingData();
+       }
+
+       /**
+        * Accepts the visitor to process the visit "request"
+        *
+        * @param       $visitorInstance        An instance of a Visitor class
+        * @return      void
+        */
+       public function accept (Visitor $visitorInstance) {
+               // Visit the assembler
+               $visitorInstance->visitAssembler($this);
+       }
+
+       /**
+        * Clears pending data
+        *
+        * @return      void
+        */
+       public function clearPendingData () {
+               // Clear it
+               $this->pendingData = '';
        }
 }