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