X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=application%2Fhub%2Fmain%2Fpackage%2Fassembler%2Fclass_PackageAssembler.php;h=0eec7aff05545a31b66cf614aed90c188b0d73c1;hb=1981eabbdff4ef8409ee73a601c226dcaf6be5b5;hp=430ed71090ac35b22d74b6b03f05eaa2970faadc;hpb=cca8969ebc91f96bde655a82db81c923d5d9a2ad;p=hub.git diff --git a/application/hub/main/package/assembler/class_PackageAssembler.php b/application/hub/main/package/assembler/class_PackageAssembler.php index 430ed7109..0eec7aff0 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 - 2011 Hub Developer Team + * @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,7 +22,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -class PackageAssembler extends BaseFrameworkSystem implements Assembler, Registerable { +class PackageAssembler extends BaseHubSystem implements Assembler, Registerable { + /** + * Pending data + */ + private $pendingData = ''; + + /** + * Private call-back methods + */ + private $callbacks = array(); + /** * Protected constructor * @@ -36,16 +46,62 @@ class PackageAssembler extends BaseFrameworkSystem implements Assembler, Registe /** * Creates an instance of this class * + * @param $packageInstance An instance of a Receivable class * @return $assemblerInstance An instance of an Assembler class */ - public static final function createPackageAssembler () { + public static final function createPackageAssembler (Receivable $packageInstance) { // Get new instance $assemblerInstance = new PackageAssembler(); + // Set package instance here + $assemblerInstance->setPackageInstance($packageInstance); + + // Create an instance of a raw data input stream + $streamInstance = ObjectFactory::createObjectByConfiguredName('node_raw_data_input_stream_class'); + + // And set it + $assemblerInstance->setInputStreamInstance($streamInstance); + + // Now get a chunk handler instance + $handlerInstance = ChunkHandlerFactory::createChunkHandlerInstance(); + + // Set handler instance + $assemblerInstance->setHandlerInstance($handlerInstance); + // Return the prepared instance return $assemblerInstance; } + /** + * Checks whether the input buffer (stacker to be more preceise) is empty. + * + * @return $isInputBufferEmpty Whether the input buffer is empty + */ + private function ifInputBufferIsEmpty () { + // Check it + $isInputBufferEmpty = $this->getPackageInstance()->getStackerInstance()->isStackEmpty(NetworkPackage::STACKER_NAME_DECODED_HANDLED); + + // Debug message + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-ASSEMBLER[' . __METHOD__ . ':' . __LINE__ . ': isInputBufferEmpty=' . intval($isInputBufferEmpty)); + + // Return it + return $isInputBufferEmpty; + } + + /** + * Checks whether given package content is completed (start/end markers are found) + * + * @param $packageContent An array with two elements: 'raw_data' and 'error_code' + * @return $isCompleted Whether the given package content is completed + */ + private function isPackageContentCompleted (array $packageContent) { + // Check both + $isCompleted = $this->ifStartEndMarkersSet($packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA]); + + // Return status + return $isCompleted; + } + /** * Assembles the content from $packageContent. This method does only * initialize the whole process by creating a call-back which will then @@ -56,59 +112,121 @@ class PackageAssembler extends BaseFrameworkSystem implements Assembler, Registe * chunks and (maybe) re-request some chunks from the sender, this would * take to much time and therefore slow down this node again. * - * @param $packageContent An array with two elements: 'decoded_data' and 'error_code' + * @param $packageContent An array with two elements: 'raw_data' and 'error_code' * @return void * @throws UnsupportedPackageCodeHandlerException If the package code handler is not implemented */ public function chunkPackageContent (array $packageContent) { // Validate the package content array again assert( - (isset($packageContent[BaseRawDataHandler::PACKAGE_DECODED_DATA])) && + (isset($packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA])) && (isset($packageContent[BaseRawDataHandler::PACKAGE_ERROR_CODE])) ); // 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); } + /************************************************************************** + * Call-back methods for above method * + **************************************************************************/ + /** - * 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: 'decoded_data' and 'error_code' + * @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[' . __METHOD__ . ':' . __LINE__ . ']: packageData[' . BaseRawDataHandler::PACKAGE_RAW_DATA . ']=' . $packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA]); + + // Check for some conditions + if ((!$this->ifInputBufferIsEmpty()) || (!$this->isPackageContentCompleted($packageContent))) { + // Last chunk is not valid, so wait for more + $this->pendingData .= $packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA]; + + // Debug message + //* 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 + /* DEBUG */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-ASSEMBLER[' . __METHOD__ . ':' . __LINE__ . ': packageContent=' . print_r($packageContent, TRUE) . ',chunks='.print_r($chunks, TRUE)); + } + } + + /** + * Checks whether the assembler's pending data is empty which means it has + * no pending data left for handling ... ;-) + * + * @return $ifPendingDataIsEmpty Whether pending data is empty + */ + public function isPendingDataEmpty () { + // A simbple check + $ifPendingDataIsEmpty = empty($this->pendingData); + + // Return it + return $ifPendingDataIsEmpty; + } + + /** + * 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 ...'); + return; + } // END - if + + // 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 = ''; + + // Debug message + //* 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 'decoded_data' with chunk separator to + * "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_DECODED_DATA]); - - // Validate final chunk - if (substr($chunks[count($chunks) - 1], 0, strlen(PackageFragmenter::END_OF_PACKAGE_IDENTIFIER)) != PackageFragmenter::END_OF_PACKAGE_IDENTIFIER) { - // Last chunk is not valid - throw new FinalChunkVerificationException(array($this, $chunks), BaseListener::EXCEPTION_FINAL_CHUNK_VERIFICATION); - } // END - if - - // Now get a chunk handler instance - $handlerInstance = ChunkHandlerFactory::createChunkHandlerInstance(); + $chunks = explode(PackageFragmenter::CHUNK_SEPARATOR, $packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA]); // 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); } }