/**
  * The daemon's main run method. It should not be necessary to override,
  * instead use the main(), iterate() and cleanup() methods to implement
  * the daemons custom functionality.
  *
  * @return void
  * @see \Thread::run()
  */
 public function run()
 {
     try {
         // register shutdown handler
         register_shutdown_function($this->getDefaultShutdownMethod());
         // bootstrap the daemon
         $this->bootstrap();
         // mark the daemon as successfully shutdown
         $this->synchronized(function ($self) {
             $self->state = EnumState::get(EnumState::RUNNING);
         }, $this);
         // keep the daemon running
         while ($this->keepRunning()) {
             try {
                 $this->iterate($this->getDefaultTimeout());
             } catch (\Exception $e) {
                 $this->log(LogLevel::ERROR, $e->__toString());
             }
         }
         // clean up the instances and free memory
         $this->cleanUp();
         // mark the daemon as successfully shutdown
         $this->synchronized(function ($self) {
             $self->state = EnumState::get(EnumState::SHUTDOWN);
         }, $this);
     } catch (\Exception $e) {
         $this->log(LogLevel::ERROR, $e->__toString());
     }
 }
 /**
  * We process the messages/jobs here.
  *
  * @return void
  */
 public function run()
 {
     try {
         // register shutdown handler
         register_shutdown_function($this->getDefaultShutdownMethod());
         // bootstrap the daemon
         $this->bootstrap();
         // synchronize the application instance and register the class loaders
         $application = $this->application;
         // mark the daemon as successfully shutdown
         $this->synchronized(function ($self) {
             $self->state = EnumState::get(EnumState::RUNNING);
         }, $this);
         // create local instances of the storages
         $messages = $this->messages;
         $priorityKey = $this->priorityKey;
         $jobsToExecute = $this->jobsToExecute;
         // load the maximum number of jobs to process in parallel
         $maximumJobsToProcess = $this->getManagerSettings()->getMaximumJobsToProcess();
         // initialize the arrays for the message states and the jobs executing
         $messageStates = array();
         $jobsExecuting = array();
         // keep the daemon running
         while ($this->keepRunning()) {
             // iterate over all job wrappers
             foreach ($jobsToExecute as $jobWrapper) {
                 try {
                     // load the message
                     $message = $messages[$jobWrapper->jobId];
                     // check if we've a message found
                     if ($message instanceof MessageInterface) {
                         // set the inital message state if not done
                         if (isset($messageStates[$jobWrapper->jobId]) === false) {
                             // initialize the default message state
                             if ($state = $message->getState()) {
                                 $messageStates[$jobWrapper->jobId] = $state->getState();
                             } else {
                                 $messageStates[$jobWrapper->jobId] = StateUnknown::KEY;
                             }
                         }
                         // check the message state
                         switch ($messageStates[$jobWrapper->jobId]) {
                             // message is active and ready to be processed
                             case StateActive::KEY:
                                 // set the new state now
                                 $messageStates[$message->getMessageId()] = StateToProcess::KEY;
                                 break;
                                 // message is paused or in progress
                             // message is paused or in progress
                             case StatePaused::KEY:
                             case StateInProgress::KEY:
                                 // make sure the job has been finished
                                 if (isset($jobsExecuting[$message->getMessageId()]) && $jobsExecuting[$message->getMessageId()] instanceof JobInterface && $jobsExecuting[$message->getMessageId()]->isFinished()) {
                                     // log a message that the job is still in progress
                                     $this->getApplication()->getInitialContext()->getSystemLogger()->info(sprintf('Job %s has been finished, remove it from job queue now', $message->getMessageId()));
                                     // set the new state now
                                     $messageStates[$message->getMessageId()] = StateProcessed::KEY;
                                 } else {
                                     // log a message that the job is still in progress
                                     $this->getApplication()->getInitialContext()->getSystemLogger()->debug(sprintf('Job %s is still in progress', $message->getMessageId()));
                                 }
                                 break;
                                 // message processing failed or has been successfully processed
                             // message processing failed or has been successfully processed
                             case StateFailed::KEY:
                             case StateProcessed::KEY:
                                 // load the unique message-ID
                                 $messageId = $message->getMessageId();
                                 // remove the job from the queue with jobs that has to be executed
                                 unset($jobsToExecute[$messageId]);
                                 // also remove the job
                                 unset($jobsExecuting[$messageId]);
                                 // finally, remove the message states and the message from the queue
                                 unset($messageStates[$messageId]);
                                 unset($messages[$messageId]);
                                 break;
                                 // message has to be processed now
                             // message has to be processed now
                             case StateToProcess::KEY:
                                 // count messages in queue
                                 $inQueue = sizeof($jobsExecuting);
                                 // we only process 200 jobs in parallel
                                 if ($inQueue < $maximumJobsToProcess) {
                                     // start the job and add it to the internal array
                                     $jobsExecuting[$message->getMessageId()] = new Job(clone $message, $application);
                                     // set the new state now
                                     $messageStates[$message->getMessageId()] = StateInProgress::KEY;
                                 } else {
                                     // log a message that queue is actually full
                                     $application->getInitialContext()->getSystemLogger()->info(sprintf('Job queue full - (%d jobs/%d msg wait)', $inQueue, sizeof($messages)));
                                     // if the job queue is full, restart iteration to remove processed jobs from queue first
                                     continue 2;
                                 }
                                 break;
                                 // message is in an unknown state -> this is weired and should never happen!
                             // message is in an unknown state -> this is weired and should never happen!
                             case StateUnknown::KEY:
                                 // set new state now
                                 $messageStates[$message->getMessageId()] = StateFailed::KEY;
                                 // log a message that we've a message with a unknown state
                                 $this->getApplication()->getInitialContext()->getSystemLogger()->critical(sprintf('Message %s has state %s', $message->getMessageId(), StateFailed::KEY));
                                 break;
                                 // we don't know the message state -> this is weired and should never happen!
                             // we don't know the message state -> this is weired and should never happen!
                             default:
                                 // set the failed message state
                                 $messageStates[$message->getMessageId()] = StateFailed::KEY;
                                 // log a message that we've a message with an invalid state
                                 $this->getApplication()->getInitialContext()->getSystemLogger()->critical(sprintf('Message %s has an invalid state', $message->getMessageId()));
                                 break;
                         }
                     }
                     // catch all exceptions
                 } catch (\Exception $e) {
                     $application->getInitialContext()->getSystemLogger()->critical($e->__toString());
                 }
                 // reduce CPU load depending on queue priority
                 $this->iterate($this->getQueueTimeout());
             }
             // reduce CPU load after each iteration
             $this->iterate($this->getDefaultTimeout());
             // profile the size of the session pool
             if ($this->profileLogger) {
                 $this->profileLogger->debug(sprintf('Processed queue worker with priority %s, size of queue size is: %d', $priorityKey, sizeof($jobsToExecute)));
             }
         }
         // clean up the instances and free memory
         $this->cleanUp();
         // mark the daemon as successfully shutdown
         $this->synchronized(function ($self) {
             $self->state = EnumState::get(EnumState::SHUTDOWN);
         }, $this);
     } catch (\Exception $e) {
         $application->getInitialContext()->getSystemLogger()->error($e->__toString());
     }
 }