]> git.mxchange.org Git - core.git/blob - framework/main/classes/handler/tasks/class_TaskHandler.php
Continued:
[core.git] / framework / main / classes / handler / tasks / class_TaskHandler.php
1 <?php
2 // Own namespace
3 namespace Org\Mxchange\CoreFramework\Handler\Task;
4
5 // Import framework stuff
6 use Org\Mxchange\CoreFramework\Bootstrap\FrameworkBootstrap;
7 use Org\Mxchange\CoreFramework\Factory\Object\ObjectFactory;
8 use Org\Mxchange\CoreFramework\Generic\FrameworkInterface;
9 use Org\Mxchange\CoreFramework\Handler\BaseHandler;
10 use Org\Mxchange\CoreFramework\Registry\Registerable;
11 use Org\Mxchange\CoreFramework\Task\Taskable;
12 use Org\Mxchange\CoreFramework\Traits\Iterator\IteratorTrait;
13 use Org\Mxchange\CoreFramework\Traits\Lists\ListableTrait;
14 use Org\Mxchange\CoreFramework\Traits\Visitor\VisitorTrait;
15 use Org\Mxchange\CoreFramework\Visitor\Visitable;
16
17 // Import SPL stuff
18 use \InvalidArgumentException;
19 use \UnexpectedValueException;
20
21 /**
22  * A Task handler
23  *
24  * @author              Roland Haeder <webmaster@shipsimu.org>
25  * @version             0.0.0
26  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2023 Core Developer Team
27  * @license             GNU GPL 3.0 or any newer version
28  * @link                http://www.shipsimu.org
29  *
30  * This program is free software: you can redistribute it and/or modify
31  * it under the terms of the GNU General Public License as published by
32  * the Free Software Foundation, either version 3 of the License, or
33  * (at your option) any later version.
34  *
35  * This program is distributed in the hope that it will be useful,
36  * but WITHOUT ANY WARRANTY; without even the implied warranty of
37  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
38  * GNU General Public License for more details.
39  *
40  * You should have received a copy of the GNU General Public License
41  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
42  */
43 class TaskHandler extends BaseHandler implements Registerable, HandleableTask {
44         // Load traits
45         use IteratorTrait;
46         use ListableTrait;
47         use VisitorTrait;
48
49         // Exception constants
50         const EXCEPTION_TASK_IS_INVALID = 0xb00;
51
52         /**
53          * Protected constructor
54          *
55          * @return      void
56          */
57         private function __construct () {
58                 // Call parent constructor
59                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('TASK-HANDLER: CONSTRUCTWD!');
60                 parent::__construct(__CLASS__);
61
62                 // Set handler name
63                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage('TASK-HANDLER: Setting handlerName=task ...');
64                 $this->setHandlerName('task');
65
66                 // Trace message
67                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('TASK-HANDLER: EXIT!');
68         }
69
70         /**
71          * Creates an instance of this class
72          *
73          * @return      $handlerInstance        An instance of a HandleableTask class
74          */
75         public static final function createTaskHandler () {
76                 // Get new instance
77                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('TASK-HANDLER: CALLED!');
78                 $handlerInstance = new TaskHandler();
79
80                 // Output debug message
81                 self::createDebugInstance(__CLASS__, __LINE__)->debugMessage('TASK-HANDLER: Initializing task handler.');
82
83                 // Init the task list
84                 $handlerInstance->setListInstance(ObjectFactory::createObjectByConfiguredName('task_list_class'));
85
86                 // Get default instance
87                 $handlerInstance->setIteratorInstance($handlerInstance->getListInstance()->getIterator());
88
89                 // Init visitor instance for faster loop
90                 $handlerInstance->setVisitorInstance(ObjectFactory::createObjectByConfiguredName('active_task_visitor_class'));
91
92                 // Register the first (and generic) idle-loop task
93                 $taskInstance = ObjectFactory::createObjectByConfiguredName('idle_task_class');
94                 $handlerInstance->registerTask('idle_loop', $taskInstance);
95
96                 // Output debug message
97                 self::createDebugInstance(__CLASS__, __LINE__)->debugMessage('TASK-HANDLER: Task handler initialized.');
98
99                 // Return the prepared instance
100                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('TASK-HANDLER:handlerInstance=%s - EXIT!', $handlerInstance->__toString()));
101                 return $handlerInstance;
102         }
103
104         /**
105          * Tries to execute the given task. If as task should not be started (yet)
106          * or the interval time (see task_interval_delay) is not yet reached the
107          * task is quietly skipped.
108          *
109          * @return      void
110          * @throws      InvalidTaskException    If the current task is invalid
111          */
112         private function executeCurrentTask () {
113                 // Update no task by default
114                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('TASK-HANDLER: CALLED!');
115                 $updateTask = false;
116
117                 // Is the current task valid?
118                 if (!$this->getListInstance()->getIterator()->valid()) {
119                         // Not valid!
120                         throw new InvalidTaskException($this, self::EXCEPTION_TASK_IS_INVALID);
121                 }
122
123                 // Get current task
124                 $currentTask = $this->getListInstance()->getIterator()->current();
125
126                 // Is the task not yet started?
127                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('TASK-HANDLER: currentTask()=%d', count($currentTask)));
128                 if ($currentTask['task_started'] === false) {
129                         // Determine difference between current time and registration
130                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('TASK-HANDLER: currentTask[id]=%s has not started yet, checking ...', $currentTask['id']));
131                         $diff = ($this->getMilliTime() - $currentTask['task_registered']) * 1000;
132
133                         // Should we start now?
134                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('TASK-HANDLER: diff=%d,currentTask[task_startup_delay]=%d', $diff, $currentTask['task_startup_delay']));
135                         if ($diff < $currentTask['task_startup_delay']) {
136                                 // Skip this silently
137                                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('TASK-HANDLER: Task %s not started: diff=%d,task_startup_delay=%d', $currentTask['id'], $diff, $currentTask['task_startup_delay']));
138                                 return;
139                         }
140
141                         // Launch the task and mark it as updated
142                         $currentTask['task_started'] = true;
143                         $updateTask = true;
144
145                         // Debug message
146                         self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('TASK-HANDLER: Task %s started with startup_delay=%dms', $currentTask['id'], $currentTask['task_startup_delay']));
147                 }
148
149                 // Get time difference from interval delay
150                 $diff = ($this->getMilliTime() - $currentTask['task_last_activity']) * 1000;
151
152                 // Is the interval delay reached?
153                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage('TASK-HANDLER: Task ' . $currentTask['id'] . ' diff=' . $diff . ',task_interval_delay=' . $currentTask['task_interval_delay'] . ',task_max_runs=' . $currentTask['task_max_runs'] . ',task_total_runs=' . $currentTask['task_total_runs']);
154                 if ((($diff < $currentTask['task_interval_delay']) && ($currentTask['task_max_runs'] == 0)) || (($currentTask['task_max_runs'] > 0) && ($currentTask['task_total_runs'] == $currentTask['task_max_runs']))) {
155                         // Should we update the task from startup?
156                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('TASK-HANDLER: currentTask[id]=%s has reached interval ... updateTask=%d', $currentTask['id'], intval($updateTask)));
157                         if ($updateTask === true) {
158                                 // Update the task before leaving
159                                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('TASK-HANDLER: Updating task %s ...', $currentTask['id']));
160                                 $this->updateTask($currentTask);
161                         }
162
163                         // Skip this silently
164                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('TASK-HANDLER: Task %s has been updated - EXIT!', $currentTask['id']));
165                         return;
166                 }
167
168                 // Set last activity
169                 $currentTask['task_last_activity'] = $this->getMilliTime();
170
171                 // Count this run
172                 $currentTask['task_total_runs']++;
173
174                 // Update the task
175                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('TASK-HANDLER: Updating task %s ...', $currentTask['id']));
176                 $this->updateTask($currentTask);
177
178                 // And visit/run it
179                 // @TODO Messurement can be added around this call
180                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('TASK-HANDLER: Invoking currentTask[task_instance]->accept(%s), task_instance=%s ...', $this->getVisitorInstance()->__toString(), $currentTask['task_instance']->__toString()));
181                 $currentTask['task_instance']->accept($this->getVisitorInstance());
182
183                 // Trace message
184                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('TASK-HANDLER: EXIT!');
185         }
186
187         /**
188          * Updates given task by updating the underlaying list
189          *
190          * @param       $taskEntry      An array with a task
191          * @return      void
192          */
193         private function updateTask (array $taskEntry) {
194                 // Get the key from current iteration
195                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('TASK-HANDLER: taskEntry()=%d - CALLED!', count($taskEntry)));
196                 $key = $this->getListInstance()->getIterator()->key();
197
198                 // Get the hash from key
199                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('TASK-HANDLER: key=%s', $key));
200                 $hash = $this->getListInstance()->getHashByIndex($key);
201
202                 // Update the entry
203                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('TASK-HANDLER: Invoking this->listInstance->updateCurrentEntryByHash(%s,taskEntry()=%d) ...', $hash, count($taskEntry)));
204                 $this->getListInstance()->updateCurrentEntryByHash($hash, $taskEntry);
205
206                 // Trace message
207                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('TASK-HANDLER: EXIT!');
208         }
209
210         /**
211          * Unregisters the given task
212          *
213          * @param       $taskData       Data of the task
214          * @return      void
215          */
216         private function unregisterTask (array $taskData) {
217                 // Remove the entry
218                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('TASK-HANDLER: Removing task %s from queue - CALLED!', $taskData['id']));
219                 $this->getListInstance()->removeEntry('tasks', $taskData);
220
221                 // Trace message
222                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('TASK-HANDLER: Removing task %s from queue - EXIT!', $taskData['id']));
223          }
224
225         /**
226          * Searches a task by given instance
227          *
228          * @param       $taskInstanc    An instanceof a Taskable class
229          * @return      $taskName               Name of the task as used while registration
230          */
231         public function searchTask (Taskable $taskInstance) {
232                 // Default is an empty (not found) task name
233                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('TASK-HANDLER: taskInstance=%s - CALLED!', $taskInstance->__toString()));
234                 $taskName = '';
235
236                 // Get whole list
237                 $taskList = $this->getListInstance()->getArrayFromList('tasks');
238
239                 // Search all instances
240                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('TASK-HANDLER: taskList()=%d', count($taskList)));
241                 foreach ($taskList as $currentTask) {
242                         // Does it match given task instance?
243                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('TASK-HANDLER: currentTask[id=%s', $currentTask['id']));
244                         if ($currentTask['task_instance']->equals($taskInstance)) {
245                                 // Found it
246                                 $taskName = $currentTask['id'];
247
248                                 // Abort here
249                                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('TASK-HANDLER: taskName=%s - BREAK!', $taskName));
250                                 break;
251                         }
252                 }
253
254                 // Return found name
255                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('TASK-HANDLER: taskName=%s - EXIT!', $taskName));
256                 return $taskName;
257         }
258
259         /**
260          * Registers a task with a task handler.
261          *
262          * @param       $taskName               A task name to register the task on
263          * @param       $taskInstance   An instance of a Taskable class
264          * @return      void
265          * @throws      InvalidArgumentException        If a parameter is not valid
266          * @throws      UnexpectedValueException        If an unexpected value has been configured
267          */
268         public function registerTask (string $taskName, Taskable $taskInstance) {
269                 // Is the parameter valid
270                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('TASK-HANDLER: taskName=%s,taskInstance - CALLED!', $taskName, $taskInstance->__toString()));
271                 if (empty($taskName)) {
272                         // Task name cannot be empty
273                         throw new InvalidArgumentException('Parameter "taskName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
274                 }
275
276                 // Get interval delay, startup delay and max runs
277                 $intervalDelay = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('task_' . $taskName . '_interval_delay');
278                 $startupDelay = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('task_' . $taskName . '_startup_delay');
279                 $maxRuns = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('task_' . $taskName . '_max_runs');
280
281                 // If the task is 'idle_loop', a deplay of zero seconds is fine
282                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('TASK-HANDLER: intervalDelay=%d,startupDelay=%d,maxRuns=%d', $intervalDelay, $startupDelay, $maxRuns));
283                 if ($intervalDelay < 0) {
284                         // Invalid configuration value
285                         throw new UnexpectedValueException(sprintf('taskName=%s has intervalDelay=%d below zero', $taskName, $intervalDelay), FrameworkInterface::EXCEPTION_UNEXPECTED_VALUE);
286                 } elseif ($startupDelay < 0) {
287                         // Invalid configuration value
288                         throw new UnexpectedValueException(sprintf('taskName=%s has startupDelay=%d below zero', $taskName, $startupDelay), FrameworkInterface::EXCEPTION_UNEXPECTED_VALUE);
289                 } elseif ($maxRuns < 0) {
290                         // Invalid configuration value
291                         throw new UnexpectedValueException(sprintf('taskName=%s has maxRuns=%d below zero', $taskName, $maxRuns), FrameworkInterface::EXCEPTION_UNEXPECTED_VALUE);
292                 } elseif ($taskName != 'idle_loop' && $intervalDelay == 0) {
293                         // Only idle_loop can have a zero interval delay
294                         throw new UnexpectedValueException(sprintf('taskName=%s has zero interval delay which is only valid for "idle_loop" task', $taskName), FrameworkInterface::EXCEPTION_UNEXPECTED_VALUE);
295                 } elseif ($taskName != 'idle_loop' && $startupDelay == 0) {
296                         // Only idle_loop can have a zero interval delay
297                         throw new UnexpectedValueException(sprintf('taskName=%s has zero startup delay which is only valid for "idle_loop" task', $taskName), FrameworkInterface::EXCEPTION_UNEXPECTED_VALUE);
298                 }
299
300                 // Create the entry
301                 $taskEntry = [
302                         // Identifier for the generateHash() method
303                         'id'                  => $taskName,
304                         // Whether the task is started
305                         'task_started'        => false,
306                         // Whether the task is paused (not yet implemented)
307                         'task_paused'         => false,
308                         // Whether the task can be paused (not yet implemented)
309                         'task_pauseable'      => true,
310                         // Timestamp of registration
311                         'task_registered'     => $this->getMilliTime(),
312                         // Last activity timestamp
313                         'task_last_activity'  => 0,
314                         // Total runs of this task
315                         'task_total_runs'     => 0,
316                         // Task instance itself
317                         'task_instance'       => $taskInstance,
318                         // Startup delay in milliseconds
319                         'task_startup_delay'  => $startupDelay,
320                         // Interval time (delay) in milliseconds before this task is executed again
321                         'task_interval_delay' => $intervalDelay,
322                         // How often should this task run?
323                         'task_max_runs'       => $maxRuns,
324                 ];
325
326                 // Add the entry
327                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('TASK-HANDLER: Invoking this->listInstance->addEntry(tasks,taskEntry()=%d) ...', count($taskEntry)));
328                 $this->getListInstance()->addEntry('tasks', $taskEntry);
329
330                 // Debug message
331                 self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('TASK-HANDLER: Task registered: taskName=%s (taskInstance=%s), startupDelay=%dms, intervalDelay=%dms, maxRuns=%d ...',
332                         $taskName,
333                         $taskInstance->__toString(),
334                         $taskEntry['task_startup_delay'],
335                         $taskEntry['task_interval_delay'],
336                         $taskEntry['task_max_runs'],
337                 ));
338
339                 // Trace message
340                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('TASK-HANDLER: EXIT!');
341         }
342
343         /**
344          * Checks whether tasks are left including idle task
345          *
346          * @return      $tasksLeft      Whether there are tasks left to handle
347          */
348         public function hasTasksLeft () {
349                 // Do we have tasks there?
350                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('TASK-HANDLER: CALLED!');
351                 $tasksLeft = ($this->getListInstance()->count() > 0);
352
353                 // Return result
354                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('TASK-HANDLER: tasksLeft=%d - EXIT!', intval($tasksLeft)));
355                 return $tasksLeft;
356         }
357
358         /**
359          * Handles all tasks by checking if they should startup or if it is their
360          * turn to run. You should use this method in a while() loop in conjuntion
361          * with hasTasksLeft() so you can e.g. shutdown by adding a ShutdownTask
362          * which will attempt to remove all tasks from the task handler.
363          *
364          * @return      void
365          */
366         public function handleTasks () {
367                 // Should we rewind?
368                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('TASK-HANDLER: CALLED!');
369                 if (!$this->getListInstance()->getIterator()->valid()) {
370                         // Rewind to the beginning for next loop
371                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('TASK-HANDLER: Invoking this->listInstance->iterator->rewind() ...');
372                         $this->getListInstance()->getIterator()->rewind();
373                 }
374
375                 // Try to execute the task
376                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('TASK-HANDLER: Invoking this->executeCurrentTask() ...');
377                 $this->executeCurrentTask();
378
379                 // Go to next entry
380                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('TASK-HANDLER: Invoking this->listInstance->iterator->next() ...');
381                 $this->getListInstance()->getIterator()->next();
382
383                 // Trace message
384                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('TASK-HANDLER: EXIT!');
385         }
386
387         /**
388          * Shuts down all tasks and the task handler itself. This method should be
389          * called from a corresponding filter class.
390          * 
391          * @return      void
392          */
393         public function doShutdown () {
394                 // Always rewind to the beginning for next loop
395                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('TASK-HANDLER: Invoking this->listInstance->iterator->rewind() - CALLED!');
396                 $this->getListInstance()->getIterator()->rewind();
397
398                 // Remember all tasks that has been shutdown for removal
399                 self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('TASK-HANDLER: Shutting down all %d tasks...', $this->getListInstance()->count()));
400                 $tasks = [];
401
402                 // Instance a visitor
403                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage('TASK-HANDLER: Setting visitorInstance=shutdown_task_visitor_class from configuration ...');
404                 $this->setVisitorInstance(ObjectFactory::createObjectByConfiguredName('shutdown_task_visitor_class'));
405
406                 // Shutdown all tasks in once go
407                 while ($this->getListInstance()->getIterator()->valid()) {
408                         // Get current entry
409                         $currentTask = $this->getListInstance()->getIterator()->current();
410
411                         // Shutdown the task
412                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage('TASK-HANDLER: Shutting down task ' . $currentTask['id'] . ' (taskInstance=' . $currentTask['task_instance']->__toString() . ') ...');
413                         $currentTask['task_instance']->accept($this->getVisitorInstance());
414
415                         // Remember this task
416                         array_push($tasks, $currentTask);
417
418                         // Advance to next one
419                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('TASK-HANDLER: Invoking this->listInstance->iterator->next() ...');
420                         $this->getListInstance()->getIterator()->next();
421                 }
422
423                 // Remove all tasks
424                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('TASK-HANDLER: Shutdown of all tasks completed. tasks()=%d', count($tasks)));
425                 foreach ($tasks as $taskEntry) {
426                         // Unregister this task
427                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('TASK-HANDLER: Invokint this->unregisterTask(taskEntry()=%d) ...', count($taskEntry)));
428                         $this->unregisterTask($taskEntry);
429                 }
430
431                 // Trace message
432                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('TASK-HANDLER: EXIT!');
433         }
434
435 }