X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=application%2Fhub%2Fmain%2Fpackage%2Fassembler%2Fclass_PackageAssembler.php;h=855cf57f0a145d7cc32cddf6894fe796fc2b1988;hb=b4558678a56b0622fe7afed4931d53737c795f2c;hp=641204f239c72ef3afdb4b89e52b8173b7182a5e;hpb=0afd1e46897791d26aba746b1e8f601bd0769b47;p=hub.git diff --git a/application/hub/main/package/assembler/class_PackageAssembler.php b/application/hub/main/package/assembler/class_PackageAssembler.php index 641204f23..855cf57f0 100644 --- a/application/hub/main/package/assembler/class_PackageAssembler.php +++ b/application/hub/main/package/assembler/class_PackageAssembler.php @@ -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 + * @author Roland Haeder * @version 0.0.0 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2012 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 @@ -22,12 +22,22 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -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; @@ -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' . $this->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,10 +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)); + //* DEBUG */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-ASSEMBLER[' . __METHOD__ . ':' . __LINE__ . ': packageContent=' . print_r($packageContent, TRUE) . ',chunks='.print_r($chunks, TRUE)); } } @@ -170,6 +194,20 @@ 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 * @@ -177,35 +215,106 @@ class PackageAssembler extends BaseHubSystem implements Assembler, Registerable */ public function handlePendingData () { // Debug output - /* NOISY-DEBUG: */ $this->debugOutput('PACKAGE-ASSEMBLER: Going to decode ' . strlen($this->pendingData) . ' Bytes of pending data. pendingData=' . $this->pendingData); + //* 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. + * "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); + //* 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()); + + // "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 = ''; } }