jobFactory = $jobFactory; $this->scheduleUtil = $scheduleUtil; $this->entityManager = $entityManager; $this->serviceFactory = $serviceFactory; $this->log = $log; } /** * Run a job entity. Does not throw exceptions. */ public function run(JobEntity $jobEntity): void { $this->runInternal($jobEntity, false); } /** * Run a job entity. Throws exceptions. * * @throws Throwable */ public function runThrowingException(JobEntity $jobEntity): void { $this->runInternal($jobEntity, true); } /** * Run a job by ID. A job must have status 'Ready'. * Used when running jobs in parallel processes. */ public function runById(string $id): void { if ($id === '') { throw new Error(); } /** @var JobEntity|null $jobEntity */ $jobEntity = $this->entityManager->getEntity(JobEntity::ENTITY_TYPE, $id); if (!$jobEntity) { throw new Error("Job '{$id}' not found."); } if ($jobEntity->getStatus() !== Status::READY) { throw new Error("Can't run job '{$id}' with no status Ready."); } $this->setJobRunning($jobEntity); $this->run($jobEntity); } private function runInternal(JobEntity $jobEntity, bool $throwException = false): void { $isSuccess = true; $exception = null; if ($jobEntity->getStatus() !== Status::RUNNING) { $this->setJobRunning($jobEntity); } try { if ($jobEntity->getScheduledJobId()) { $this->runScheduledJob($jobEntity); } else if ($jobEntity->getJob()) { $this->runJobNamed($jobEntity); } else if ($jobEntity->getClassName()) { $this->runJobWithClassName($jobEntity); } else if ($jobEntity->getServiceName()) { $this->runService($jobEntity); } else { $id = $jobEntity->getId(); throw new Error("Not runnable job '{$id}'."); } } catch (Throwable $e) { $isSuccess = false; $this->log->error( "JobManager: Failed job running, job '{$jobEntity->id}'. " . $e->getMessage() . "; at " . $e->getFile() . ":" . $e->getLine() . "." ); if ($throwException) { $exception = $e; } } $status = $isSuccess ? Status::SUCCESS : Status::FAILED; $jobEntity->set('status', $status); if ($isSuccess) { $jobEntity->set('executedAt', DateTimeUtil::getSystemNowString()); } $this->entityManager->saveEntity($jobEntity); if ($throwException && $exception) { throw new $exception($exception->getMessage()); } if ($jobEntity->getScheduledJobId()) { $this->scheduleUtil->addLogRecord( $jobEntity->getScheduledJobId(), $status, null, $jobEntity->getTargetId(), $jobEntity->getTargetType() ); } } private function runJobNamed(JobEntity $jobEntity): void { $jobName = $jobEntity->getJob(); $job = $this->jobFactory->create($jobName); $this->runJob($job, $jobEntity); } private function runScheduledJob(JobEntity $jobEntity): void { $jobName = $jobEntity->getScheduledJobJob(); if (!$jobName) { throw new Error( "Can't run job '" . $jobEntity->getId() . "'. Not a scheduled job." ); } $job = $this->jobFactory->create($jobName); $this->runJob($job, $jobEntity); } private function runJobWithClassName(JobEntity $jobEntity): void { $className = $jobEntity->getClassName(); $job = $this->jobFactory->createByClassName($className); $this->runJob($job, $jobEntity); } /** * @param Job|JobDataLess $job */ private function runJob($job, JobEntity $jobEntity): void { $data = Data::create($jobEntity->getData()) ->withTargetId($jobEntity->getTargetId()) ->withTargetType($jobEntity->getTargetType()); $job->run($data); } private function runService(JobEntity $jobEntity): void { $serviceName = $jobEntity->getServiceName(); if (!$serviceName) { throw new Error("Job with empty serviceName."); } if (!$this->serviceFactory->checkExists($serviceName)) { throw new Error(); } $service = $this->serviceFactory->create($serviceName); $methodName = $jobEntity->getMethodName(); if (!$methodName) { throw new Error('Job with empty methodName.'); } if (!method_exists($service, $methodName)) { throw new Error("No method '{$methodName}' in service '{$serviceName}'."); } $service->$methodName($jobEntity->getData(), $jobEntity->getTargetId(), $jobEntity->getTargetType()); } private function setJobRunning(JobEntity $jobEntity): void { if (!$jobEntity->getStartedAt()) { $jobEntity->set('startedAt', DateTimeUtil::getSystemNowString()); } $jobEntity->set('status', Status::RUNNING); $jobEntity->set('pid', System::getPid()); $this->entityManager->saveEntity($jobEntity); } }