]> git.mxchange.org Git - hub.git/blob - application/hub/main/package/assembler/class_PackageAssembler.php
Improved debug lines. All should be like these!
[hub.git] / application / hub / main / package / assembler / class_PackageAssembler.php
1 <?php
2 /**
3  * A PackageAssembler class to assemble a package content stream fragemented
4  * by PackageFragmenter back to a raw package data array.
5  *
6  * @author              Roland Haeder <webmaster@shipsimu.org>
7  * @version             0.0.0
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.shipsimu.org
11  *
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.
16  *
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.
21  *
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/>.
24  */
25 class PackageAssembler extends BaseHubSystem implements Assembler, Registerable {
26         /**
27          * Pending data
28          */
29         private $pendingData = '';
30
31         /**
32          * Private call-back methods
33          */
34         private $callbacks = array();
35
36         /**
37          * Protected constructor
38          *
39          * @return      void
40          */
41         protected function __construct () {
42                 // Call parent constructor
43                 parent::__construct(__CLASS__);
44         }
45
46         /**
47          * Creates an instance of this class
48          *
49          * @param       $packageInstance        An instance of a Receivable class
50          * @return      $assemblerInstance      An instance of an Assembler class
51          */
52         public static final function createPackageAssembler (Receivable $packageInstance) {
53                 // Get new instance
54                 $assemblerInstance = new PackageAssembler();
55
56                 // Set package instance here
57                 $assemblerInstance->setPackageInstance($packageInstance);
58
59                 // Create an instance of a raw data input stream
60                 $streamInstance = ObjectFactory::createObjectByConfiguredName('node_raw_data_input_stream_class');
61
62                 // And set it
63                 $assemblerInstance->setInputStreamInstance($streamInstance);
64
65                 // Now get a chunk handler instance
66                 $handlerInstance = ChunkHandlerFactory::createChunkHandlerInstance();
67
68                 // Set handler instance
69                 $assemblerInstance->setHandlerInstance($handlerInstance);
70
71                 // Return the prepared instance
72                 return $assemblerInstance;
73         }
74
75         /**
76          * Checks whether the input buffer (stacker to be more preceise) is empty.
77          *
78          * @return      $isInputBufferEmpty             Whether the input buffer is empty
79          */
80         private function ifInputBufferIsEmpty () {
81                 // Check it
82                 $isInputBufferEmpty = $this->getPackageInstance()->getStackerInstance()->isStackEmpty(NetworkPackage::STACKER_NAME_DECODED_HANDLED);
83
84                 // Debug message
85                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-ASSEMBLER[' . __METHOD__ . ':' . __LINE__ . ': isInputBufferEmpty=' . intval($isInputBufferEmpty));
86
87                 // Return it
88                 return $isInputBufferEmpty;
89         }
90
91         /**
92          * Checks whether given package content is completed (start/end markers are found)
93          *
94          * @param       $packageContent         An array with two elements: 'raw_data' and 'error_code'
95          * @return      $isCompleted            Whether the given package content is completed
96          */
97         private function isPackageContentCompleted (array $packageContent) {
98                 // Check both
99                 $isCompleted = $this->ifStartEndMarkersSet($packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA]);
100
101                 // Return status
102                 return $isCompleted;
103         }
104
105         /**
106          * Assembles the content from $packageContent. This method does only
107          * initialize the whole process by creating a call-back which will then
108          * itself (99.9% of all cases) "explode" the decoded data stream and add
109          * it to a chunk assembler queue.
110          *
111          * If the call-back method or this would attempt to assemble the package
112          * chunks and (maybe) re-request some chunks from the sender, this would
113          * take to much time and therefore slow down this node again.
114          *
115          * @param       $packageContent         An array with two elements: 'raw_data' and 'error_code'
116          * @return      void
117          * @throws      UnsupportedPackageCodeHandlerException  If the package code handler is not implemented
118          */
119         public function chunkPackageContent (array $packageContent) {
120                 // Validate the package content array again
121                 assert(
122                         (isset($packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA])) &&
123                         (isset($packageContent[BaseRawDataHandler::PACKAGE_ERROR_CODE]))
124                 );
125
126                 // Construct call-back name from package error code
127                 $this->callbacks[$packageContent[BaseRawDataHandler::PACKAGE_ERROR_CODE]] = 'handlePackageBy' . $this->convertToClassName($packageContent[BaseRawDataHandler::PACKAGE_ERROR_CODE]);
128
129                 // Abort if the call-back method is not there
130                 if (!method_exists($this, $this->callbacks[$packageContent[BaseRawDataHandler::PACKAGE_ERROR_CODE]])) {
131                         // Throw an exception
132                         throw new UnsupportedPackageCodeHandlerException(array($this, $this->callbacks[$packageContent[BaseRawDataHandler::PACKAGE_ERROR_CODE]], $packageContent), BaseListener::EXCEPTION_UNSUPPORTED_PACKAGE_CODE_HANDLER);
133                 } // END - if
134
135                 // Call it back
136                 call_user_func(array($this, $this->callbacks[$packageContent[BaseRawDataHandler::PACKAGE_ERROR_CODE]]), $packageContent);
137         }
138
139         /**************************************************************************
140          *                 Call-back methods for above method                     *
141          **************************************************************************/
142
143         /**
144          * Call-back handler to handle unhandled package data. This method
145          * "explodes" the string with the chunk separator from PackageFragmenter
146          * class, does some low checks on it and feeds it into another queue for
147          * verification and re-request for bad chunks.
148          *
149          * @param       $packageContent         An array with two elements: 'raw_data' and 'error_code'
150          * @return      void
151          */
152         private function handlePackageByUnhandledPackage (array $packageContent) {
153                 // Debug message
154                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-ASSEMBLER[' . __METHOD__ . ':' . __LINE__ . ']: packageData[' . BaseRawDataHandler::PACKAGE_RAW_DATA . ']=' . $packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA]);
155
156                 // Check for some conditions
157                 if ((!$this->ifInputBufferIsEmpty()) || (!$this->isPackageContentCompleted($packageContent))) {
158                         // Last chunk is not valid, so wait for more
159                         $this->pendingData .= $packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA];
160
161                         // Debug message
162                         /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-ASSEMBLER[' . __METHOD__ . ':' . __LINE__ . ': Partial data received. Waiting for more ... ( ' . strlen($packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA]) . ' bytes)');
163                 } else {
164                         // Debug message
165                         /* DEBUG */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-ASSEMBLER[' . __METHOD__ . ':' . __LINE__ . ': packageContent=' . print_r($packageContent, TRUE) . ',chunks='.print_r($chunks, TRUE));
166                 }
167         }
168
169         /**
170          * Checks whether the assembler's pending data is empty which means it has
171          * no pending data left for handling ... ;-)
172          *
173          * @return      $ifPendingDataIsEmpty   Whether pending data is empty
174          */
175         public function isPendingDataEmpty () {
176                 // A simbple check
177                 $ifPendingDataIsEmpty = empty($this->pendingData);
178
179                 // Return it
180                 return $ifPendingDataIsEmpty;
181         }
182
183         /**
184          * Handles the assembler's pending data
185          *
186          * @return      void
187          */
188         public function handlePendingData () {
189                 // Debug output
190                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-ASSEMBLER[' . __METHOD__ . ':' . __LINE__ . ': Going to decode ' . strlen($this->pendingData) . ' Bytes of pending data. pendingData=' . $this->pendingData);
191
192                 // Assert on condition
193                 assert(!$this->isPendingDataEmpty());
194
195                 // Init fake array
196                 $packageContent = array(
197                         BaseRawDataHandler::PACKAGE_RAW_DATA   => $this->getInputStreamInstance()->streamData($this->pendingData),
198                         BaseRawDataHandler::PACKAGE_ERROR_CODE => BaseRawDataHandler::SOCKET_ERROR_UNHANDLED
199                 );
200
201                 // Clear pending data
202                 $this->pendingData = '';
203
204                 // Debug message
205                 /* 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.');
206
207                 // Make sure last CHUNK_SEPARATOR is not there
208                 if (substr($packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA], -1, 1) == PackageFragmenter::CHUNK_SEPARATOR) {
209                         // Remove it
210                         $packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA] = substr($packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA], 0, -1);
211                 } // END - if
212
213                 /*
214                  * "explode" the string from 'raw_data' with chunk separator to
215                  * get an array of chunks. These chunks must then be verified by
216                  * their checksums. Also the final chunk must be handled.
217                  */
218                 $chunks = explode(PackageFragmenter::CHUNK_SEPARATOR, $packageContent[BaseRawDataHandler::PACKAGE_RAW_DATA]);
219
220                 // Add all chunks because the last final chunk is found
221                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('PACKAGE-ASSEMBLER[' . __METHOD__ . ':' . __LINE__ . ': Going to add ' . count($chunks) . ' to chunk handler ...');
222                 $this->getHandlerInstance()->addAllChunksWithFinal($chunks);
223         }
224 }
225
226 // [EOF]
227 ?>