3 * A PackageAssembler class to assemble a package content stream fragemented
4 * by PackageFragmenter back to a raw package data array.
6 * @author Roland Haeder <webmaster@ship-simu.org>
8 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2012 Hub Developer Team
9 * @license GNU GPL 3.0 or any newer version
10 * @link http://www.ship-simu.org
12 * This program is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 class PackageAssembler extends BaseHubSystem implements Assembler, Registerable {
29 private $pendingData = '';
32 * Protected constructor
36 protected function __construct () {
37 // Call parent constructor
38 parent::__construct(__CLASS__);
42 * Creates an instance of this class
44 * @param $packageInstance An instance of a Receivable class
45 * @return $assemblerInstance An instance of an Assembler class
47 public static final function createPackageAssembler (Receivable $packageInstance) {
49 $assemblerInstance = new PackageAssembler();
51 // Set package instance here
52 $assemblerInstance->setPackageInstance($packageInstance);
54 // Return the prepared instance
55 return $assemblerInstance;
59 * Checks whether the input buffer (stacker to be more preceise) is empty.
61 * @return $isInputBufferEmpty Whether the input buffer is empty
63 private function ifInputBufferIsEmpty () {
65 $isInputBufferEmpty = $this->getPackageInstance()->getStackerInstance()->isStackEmpty(NetworkPackage::STACKER_NAME_DECODED_HANDLED);
68 //* NOISY-DEBUG: */ $this->debugOutput('PACKAGE-ASSEMBLER: isInputBufferEmpty=' . intval($isInputBufferEmpty));
71 return $isInputBufferEmpty;
75 * Assembles the content from $packageContent. This method does only
76 * initialize the whole process by creating a call-back which will then
77 * itself (99.9% of all cases) "explode" the decoded data stream and add
78 * it to a chunk assembler queue.
80 * If the call-back method or this would attempt to assemble the package
81 * chunks and (maybe) re-request some chunks from the sender, this would
82 * take to much time and therefore slow down this node again.
84 * @param $packageContent An array with two elements: 'decoded_data' and 'error_code'
86 * @throws UnsupportedPackageCodeHandlerException If the package code handler is not implemented
88 public function chunkPackageContent (array $packageContent) {
89 // Validate the package content array again
91 (isset($packageContent[BaseRawDataHandler::PACKAGE_DECODED_DATA])) &&
92 (isset($packageContent[BaseRawDataHandler::PACKAGE_ERROR_CODE]))
95 // Construct call-back name from package error code
96 $methodName = 'handlePackageBy' . $this->convertToClassName($packageContent[BaseRawDataHandler::PACKAGE_ERROR_CODE]);
98 // Abort if the call-back method is not there
99 if (!method_exists($this, $methodName)) {
100 // Throw an exception
101 throw new UnsupportedPackageCodeHandlerException(array($this, $methodName, $packageContent), BaseListener::EXCEPTION_UNSUPPORTED_PACKAGE_CODE_HANDLER);
105 call_user_func(array($this, $methodName), $packageContent);
108 /**************************************************************************
109 * Call-back methods for above method *
110 **************************************************************************/
113 * Call-back handler to handle unhandled packages. This method "explodes"
114 * the string with the chunk separator from PackageFragmenter class, does
115 * some low checks on it and feeds it into another queue for verification
116 * and re-request for bad chunks.
118 * @param $packageContent An array with two elements: 'decoded_data' and 'error_code'
120 * @throws FinalChunkVerificationException If the final chunk does not start with 'EOP:'
122 private function handlePackageByUnhandledPackage (array $packageContent) {
124 /* NOISY-DEBUG: */ $this->debugOutput('PACKAGE-ASSEMBLER: packageData[' . BaseRawDataHandler::PACKAGE_DECODED_DATA . ']=' . $packageContent[BaseRawDataHandler::PACKAGE_DECODED_DATA]);
126 // Check for some conditions
127 if (!$this->ifInputBufferIsEmpty()) {
128 // Last chunk is not valid, so wait for more
129 $this->pendingData .= $packageContent[BaseRawDataHandler::PACKAGE_DECODED_DATA];
132 /* NOISY-DEBUG: */ $this->debugOutput('PACKAGE-ASSEMBLER: Partial data received. Waiting for more ... ( ' . strlen($packageContent[BaseRawDataHandler::PACKAGE_DECODED_DATA]) . ' bytes)');
135 //* NOISY-DEBUG */ $this->debugOutput('packageContent=' . print_r($packageContent,true) . ',chunks='.print_r($chunks,true));
138 * "explode" the string from 'decoded_data' with chunk separator to
139 * get an array of chunks. These chunks must then be verified by
140 * their checksums. Also the final chunk must be handled.
142 $chunks = explode(PackageFragmenter::CHUNK_SEPARATOR, $packageContent[BaseRawDataHandler::PACKAGE_DECODED_DATA]);
144 // Now get a chunk handler instance
145 $handlerInstance = ChunkHandlerFactory::createChunkHandlerInstance();
147 // Add all chunks because the last final chunk is found
148 $handlerInstance->addAllChunksWithFinal($chunks);
153 * Checks whether the assembler's pending data is empty which means it has
154 * no pending data left for handling ... ;-)
156 * @return $ifPendingDataIsEmpty Whether pending data is empty
158 public function isPendingDataEmpty () {
160 $ifPendingDataIsEmpty = empty($this->pendingData);
163 return $ifPendingDataIsEmpty;
167 * Handles the assembler's pending data
171 public function handlePendingData () {
172 // Assert on condition
173 assert(!$this->isPendingDataEmpty());
176 $packageContent = array(
177 BaseRawDataHandler::PACKAGE_DECODED_DATA => $this->pendingData,
178 BaseRawDataHandler::PACKAGE_ERROR_CODE => BaseRawDataHandler::SOCKET_ERROR_UNHANDLED
181 // Clear pending data
182 $this->pendingData = '';
185 /* NOISY-DEBUG: */ $this->debugOutput('PACKAGE-ASSEMBLER: Last block of partial data received. A total of ' . strlen($packageContent[BaseRawDataHandler::PACKAGE_DECODED_DATA]) . ' bytes has been received.');
187 // Call the real handler method
188 $this->handlePackageByUnhandledPackage($packageContent);