diff --git a/application/Espo/Core/CronManager.php b/application/Espo/Core/CronManager.php index 6d3984805a..5ee20d1962 100644 --- a/application/Espo/Core/CronManager.php +++ b/application/Espo/Core/CronManager.php @@ -44,6 +44,10 @@ class CronManager private $scheduledJobUtil; + private $cronJobUtil; + + private $cronScheduledJobUtil; + const PENDING = 'Pending'; const RUNNING = 'Running'; @@ -64,8 +68,8 @@ class CronManager $this->serviceFactory = $this->container->get('serviceFactory'); $this->scheduledJobUtil = $this->container->get('scheduledJob'); - $this->cronJob = new \Espo\Core\Utils\Cron\Job($this->config, $this->entityManager); - $this->cronScheduledJob = new \Espo\Core\Utils\Cron\ScheduledJob($this->config, $this->entityManager); + $this->cronJobUtil = new \Espo\Core\Utils\Cron\Job($this->config, $this->entityManager); + $this->cronScheduledJobUtil = new \Espo\Core\Utils\Cron\ScheduledJob($this->config, $this->entityManager); } protected function getContainer() @@ -98,14 +102,14 @@ class CronManager return $this->scheduledJobUtil; } - protected function getCronJob() + protected function getCronJobUtil() { - return $this->cronJob; + return $this->cronJobUtil; } - protected function getCronScheduledJob() + protected function getCronScheduledJobUtil() { - return $this->cronScheduledJob; + return $this->cronScheduledJobUtil; } protected function getLastRunTime() @@ -155,58 +159,44 @@ class CronManager $this->setLastRunTime(time()); - $this->getCronJob()->markFailedJobs(); - $this->getCronJob()->updateFailedJobAttempts(); + $this->getCronJobUtil()->markFailedJobs(); + $this->getCronJobUtil()->updateFailedJobAttempts(); $this->createJobsFromScheduledJobs(); - $this->getCronJob()->removePendingJobDuplicates(); + $this->getCronJobUtil()->removePendingJobDuplicates(); - $pendingJobList = $this->getCronJob()->getPendingJobList(); + $pendingJobList = $this->getCronJobUtil()->getPendingJobList(); foreach ($pendingJobList as $job) { - $jobEntity = $this->getEntityManager()->getEntity('Job', $job['id']); - - if (!isset($jobEntity)) { - $GLOBALS['log']->error('CronManager: empty Job entity ['.$job['id'].'].'); - continue; - } - - $jobEntity->set('status', self::RUNNING); - $this->getEntityManager()->saveEntity($jobEntity); + $job->set('status', self::RUNNING); + $this->getEntityManager()->saveEntity($job); $isSuccess = true; try { - if (!empty($job['scheduled_job_id'])) { + if ($job->get('scheduledJobId')) { $this->runScheduledJob($job); } else { $this->runService($job); } } catch (\Exception $e) { $isSuccess = false; - $GLOBALS['log']->error('CronManager: Failed job running, job ['.$job['id'].']. Error Details: '.$e->getMessage()); + $GLOBALS['log']->error('CronManager: Failed job running, job ['.$job->id.']. Error Details: '.$e->getMessage()); } $status = $isSuccess ? self::SUCCESS : self::FAILED; - $jobEntity->set('status', $status); - $this->getEntityManager()->saveEntity($jobEntity); + $job->set('status', $status); + $this->getEntityManager()->saveEntity($job); - if (!empty($job['scheduled_job_id'])) { - $this->getCronScheduledJob()->addLogRecord($job['scheduled_job_id'], $status, null, $job['target_id'], $job['target_type']); + if ($job->get('scheduledJobId')) { + $this->getCronScheduledJobUtil()->addLogRecord($job->get('scheduledJobId'), $status, null, $job->get('targetId'), $job->get('targetType')); } } } - /** - * Run Scheduled Job - * - * @param array $job - * - * @return void - */ - protected function runScheduledJob(array $job) + protected function runScheduledJob($job) { - $jobName = $job['method']; + $jobName = $job->get('scheduledJobJob'); $className = $this->getScheduledJobUtil()->get($jobName); if ($className === false) { @@ -220,14 +210,12 @@ class CronManager } $data = null; - if (!empty($job['data'])) { - $data = $job['data']; - if (Json::isJSON($data)) { - $data = Json::decode($data, true); - } + + if ($job->get('data')) { + $data = $job->get('data'); } - $jobClass->$method($data, $job['target_id'], $job['target_type']); + $jobClass->$method($data, $job->get('targetId'), $job->get('targetType')); } /** @@ -237,62 +225,74 @@ class CronManager * * @return void */ - protected function runService(array $job) + protected function runService($job) { - $serviceName = $job['service_name']; + $serviceName = $job->get('serviceName'); + + if (!$serviceName) { + throw new Error('Job with empty serviceName.'); + } if (!$this->getServiceFactory()->checkExists($serviceName)) { throw new NotFound(); } $service = $this->getServiceFactory()->create($serviceName); - $serviceMethod = $job['method']; - if (!method_exists($service, $serviceMethod)) { + $methodNameDeprecated = $job->get('method'); + $methodName = $job->get('methodName'); + + $isDeprecated = false; + if (!$methodName) { + $isDeprecated = true; + $methodName = $methodNameDeprecated; + } + + if (!$methodName) { + throw new Error('Job with empty methodName.'); + } + + if (!method_exists($service, $methodName)) { throw new NotFound(); } - $data = $job['data']; - if (Json::isJSON($data)) { - $data = Json::decode($data, true); + $data = $job->get('data'); + + if ($isDeprecated) { + $data = Json::decode(Json::encode($data), true); } - $service->$serviceMethod($data, $job['target_id'], $job['target_type']); + $service->$methodName($data, $job->get('targetId'), $job->get('targetType')); } - /** - * Check scheduled jobs and create related jobs - * - * @return array List of created Jobs - */ protected function createJobsFromScheduledJobs() { - $activeScheduledJobList = $this->getCronScheduledJob()->getActiveScheduledJobList(); + $activeScheduledJobList = $this->getCronScheduledJobUtil()->getActiveScheduledJobList(); - $runningScheduledJobIdList = $this->getCronJob()->getRunningScheduledJobIdList(); + $runningScheduledJobIdList = $this->getCronJobUtil()->getRunningScheduledJobIdList(); $createdJobIdList = array(); foreach ($activeScheduledJobList as $scheduledJob) { - $scheduling = $scheduledJob['scheduling']; + $scheduling = $scheduledJob->get('scheduling'); try { $cronExpression = \Cron\CronExpression::factory($scheduling); } catch (\Exception $e) { - $GLOBALS['log']->error('CronManager (ScheduledJob ['.$scheduledJob['id'].']): Scheduling string error - '. $e->getMessage() . '.'); + $GLOBALS['log']->error('CronManager (ScheduledJob ['.$scheduledJob->id.']): Scheduling string error - '. $e->getMessage() . '.'); continue; } try { $nextDate = $cronExpression->getNextRunDate()->format('Y-m-d H:i:s'); } catch (\Exception $e) { - $GLOBALS['log']->error('CronManager (ScheduledJob ['.$scheduledJob['id'].']): Unsupported CRON expression ['.$scheduling.']'); + $GLOBALS['log']->error('CronManager (ScheduledJob ['.$scheduledJob->id.']): Unsupported CRON expression ['.$scheduling.']'); continue; } - $existingJob = $this->getCronJob()->getJobByScheduledJob($scheduledJob['id'], $nextDate); + $existingJob = $this->getCronJobUtil()->getJobByScheduledJob($scheduledJob->id, $nextDate); if ($existingJob) continue; - $className = $this->getScheduledJobUtil()->get($scheduledJob['job']); + $className = $this->getScheduledJobUtil()->get($scheduledJob->get('job')); if ($className) { if (method_exists($className, 'prepare')) { $implementation = new $className($this->container); @@ -301,24 +301,18 @@ class CronManager } } - if (in_array($scheduledJob['id'], $runningScheduledJobIdList)) { + if (in_array($scheduledJob->id, $runningScheduledJobIdList)) { continue; } $jobEntity = $this->getEntityManager()->getEntity('Job'); $jobEntity->set(array( - 'name' => $scheduledJob['name'], + 'name' => $scheduledJob->get('name'), 'status' => self::PENDING, - 'scheduledJobId' => $scheduledJob['id'], - 'executeTime' => $nextDate, - 'method' => $scheduledJob['job'] + 'scheduledJobId' => $scheduledJob->id, + 'executeTime' => $nextDate )); $this->getEntityManager()->saveEntity($jobEntity); - - $createdJobIdList[] = $jobEntity->id; } - - return $createdJobIdList; } } - diff --git a/application/Espo/Core/Utils/Cron/Job.php b/application/Espo/Core/Utils/Cron/Job.php index 881a10f703..7d7abf3494 100644 --- a/application/Espo/Core/Utils/Cron/Job.php +++ b/application/Espo/Core/Utils/Cron/Job.php @@ -74,18 +74,39 @@ class Job $jobConfigs = $this->getConfig()->get('cron'); $limit = !empty($jobConfigs['maxJobNumber']) ? intval($jobConfigs['maxJobNumber']) : 0; - $jobList = $this->getJobList(CronManager::PENDING, $limit); + $selectParams = [ + 'select' => [ + 'id', + 'scheduledJobId', + 'scheduledJobJob', + 'executeTime', + 'targetId', + 'targetType', + 'methodName', + 'method', // TODO remove deprecated + 'serviceName' + ], + 'whereClause' => [ + 'status' => 'Pending', + 'executeTime<=' => date('Y-m-d H:i:s') + ], + 'orderBy' => 'executeTime' + ]; + if ($limit) { + $selectParams['offset'] = 0; + $selectParams['limit'] = $limit; + } + $jobList = $this->getEntityManager()->getRepository('Job')->find($selectParams); $runningScheduledJobIdList = $this->getRunningScheduledJobIdList(); - $list = []; - foreach ($jobList as $row) { - if (!in_array($row['scheduled_job_id'], $runningScheduledJobIdList)) { - $list[] = $row; - } + $actualJobList = []; + foreach ($jobList as $job) { + if ($job->get('scheduledJobId') && in_array($job->get('scheduledJobId'), $runningScheduledJobIdList)) continue; + $actualJobList[] = $job; } - return $list; + return $actualJobList; } public function getRunningScheduledJobIdList() @@ -114,41 +135,6 @@ class Job return $list; } - /** - * Get job list, which execution date in jobPeriod time - * - * @param string $status - * @param int $limit - * - * @return array - */ - public function getJobList($status = CronManager::PENDING, $limit = null) - { - $currentTime = time(); - - $pdo = $this->getEntityManager()->getPDO(); - - $query = " - SELECT * FROM job - WHERE - `status` = " . $pdo->quote($status) . " - AND execute_time <= '".date('Y-m-d H:i:s', $currentTime)."' - AND deleted = 0 - ORDER BY execute_time ASC - "; - - if ($limit) { - $query .= " LIMIT " . $limit ; - } - - $sth = $pdo->prepare($query); - $sth->execute(); - - $rows = $sth->fetchAll(PDO::FETCH_ASSOC); - - return $rows; - } - /** * Get Jobs by ScheduledJobId and date * diff --git a/application/Espo/Core/Utils/Cron/ScheduledJob.php b/application/Espo/Core/Utils/Cron/ScheduledJob.php index 13653dfd32..4c78b3303c 100644 --- a/application/Espo/Core/Utils/Cron/ScheduledJob.php +++ b/application/Espo/Core/Utils/Cron/ScheduledJob.php @@ -55,31 +55,17 @@ class ScheduledJob } /** - * Get active Scheduler Jobs + * Get active Scheduler Job List * - * @return array + * @return EntityCollection */ public function getActiveScheduledJobList() { - $query = " - SELECT * FROM scheduled_job - WHERE - `status` = 'Active' AND - deleted = 0 - "; - - $pdo = $this->getEntityManager()->getPDO(); - $sth = $pdo->prepare($query); - $sth->execute(); - - $rows = $sth->fetchAll(PDO::FETCH_ASSOC); - - $list = array(); - foreach ($rows as $row) { - $list[] = $row; - } - - return $list; + return $this->getEntityManager()->getRepository('ScheduledJob')->select([ + 'id', 'scheduling', 'job', 'name' + ])->where([ + 'status' => 'Active' + ])->find(); } /** diff --git a/application/Espo/Hooks/Common/AssignmentEmailNotification.php b/application/Espo/Hooks/Common/AssignmentEmailNotification.php index cf07d5c3d2..70fc264ef9 100644 --- a/application/Espo/Hooks/Common/AssignmentEmailNotification.php +++ b/application/Espo/Hooks/Common/AssignmentEmailNotification.php @@ -51,12 +51,12 @@ class AssignmentEmailNotification extends \Espo\Core\Hooks\Base $job = $this->getEntityManager()->getEntity('Job'); $job->set(array( 'serviceName' => 'EmailNotification', - 'method' => 'notifyAboutAssignmentJob', + 'methodName' => 'notifyAboutAssignmentJob', 'data' => json_encode(array( 'userId' => $userId, 'assignerUserId' => $this->getUser()->id, 'entityId' => $entity->id, - 'entityType' => $entity->getEntityName() + 'entityType' => $entity->getEntityType() )), 'executeTime' => date('Y-m-d H:i:s'), )); @@ -64,6 +64,4 @@ class AssignmentEmailNotification extends \Espo\Core\Hooks\Base } } } - } - diff --git a/application/Espo/Hooks/Common/Stream.php b/application/Espo/Hooks/Common/Stream.php index c6dcb862bf..f6efd69873 100644 --- a/application/Espo/Hooks/Common/Stream.php +++ b/application/Espo/Hooks/Common/Stream.php @@ -238,7 +238,7 @@ class Stream extends \Espo\Core\Hooks\Base $job = $this->getEntityManager()->getEntity('Job'); $job->set(array( 'serviceName' => 'Stream', - 'method' => 'afterRecordCreatedJob', + 'methodName' => 'afterRecordCreatedJob', 'data' => array( 'userIdList' => $autofollowUserIdList, 'entityType' => $entity->getEntityType(), @@ -247,7 +247,6 @@ class Stream extends \Espo\Core\Hooks\Base )); $this->getEntityManager()->saveEntity($job); } - } else { if (empty($options['noStream']) && empty($options['silent'])) { if ($entity->isFieldChanged('assignedUserId')) { @@ -297,7 +296,7 @@ class Stream extends \Espo\Core\Hooks\Base $job = $this->getEntityManager()->getEntity('Job'); $job->set(array( 'serviceName' => 'Stream', - 'method' => 'controlFollowersJob', + 'methodName' => 'controlFollowersJob', 'data' => array( 'entityType' => $entity->getEntityType(), 'entityId' => $entity->id @@ -306,7 +305,6 @@ class Stream extends \Espo\Core\Hooks\Base $this->getEntityManager()->saveEntity($job); } } - } if ($entity->isNew() && empty($options['noStream']) && empty($options['silent']) && $this->getMetadata()->get(['scopes', $entityType, 'object'])) { diff --git a/application/Espo/Modules/Crm/Jobs/CheckEmailAccounts.php b/application/Espo/Modules/Crm/Jobs/CheckEmailAccounts.php index 5ad82e5975..d457e954f8 100644 --- a/application/Espo/Modules/Crm/Jobs/CheckEmailAccounts.php +++ b/application/Espo/Modules/Crm/Jobs/CheckEmailAccounts.php @@ -58,14 +58,14 @@ class CheckEmailAccounts extends \Espo\Core\Jobs\Base return true; } - public function prepare($data, $executeTime) + public function prepare($scheduledJob, $executeTime) { $collection = $this->getEntityManager()->getRepository('EmailAccount')->where(array( 'status' => 'Active' ))->find(); foreach ($collection as $entity) { $running = $this->getEntityManager()->getRepository('Job')->where(array( - 'scheduledJobId' => $data['id'], + 'scheduledJobId' => $scheduledJob->id, 'status' => 'Running', 'targetType' => 'EmailAccount', 'targetId' => $entity->id @@ -73,21 +73,18 @@ class CheckEmailAccounts extends \Espo\Core\Jobs\Base if ($running) continue; $countPending = $this->getEntityManager()->getRepository('Job')->where(array( - 'scheduledJobId' => $data['id'], + 'scheduledJobId' => $scheduledJob->id, 'status' => 'Pending', 'targetType' => 'EmailAccount', 'targetId' => $entity->id ))->count(); if ($countPending > 1) continue; - $job = $this->getEntityManager()->getEntity('Job'); - $jobEntity = $this->getEntityManager()->getEntity('Job'); $jobEntity->set(array( - 'name' => $data['name'], - 'scheduledJobId' => $data['id'], + 'name' => $scheduledJob->get('name'), + 'scheduledJobId' => $scheduledJob->id, 'executeTime' => $executeTime, - 'method' => 'CheckEmailAccounts', 'targetType' => 'EmailAccount', 'targetId' => $entity->id )); @@ -97,4 +94,3 @@ class CheckEmailAccounts extends \Espo\Core\Jobs\Base return true; } } - diff --git a/application/Espo/Resources/i18n/en_US/Job.json b/application/Espo/Resources/i18n/en_US/Job.json index c9f95a78e8..9b31ad6a3a 100644 --- a/application/Espo/Resources/i18n/en_US/Job.json +++ b/application/Espo/Resources/i18n/en_US/Job.json @@ -5,8 +5,10 @@ "attempts": "Attempts Left", "failedAttempts": "Failed Attempts", "serviceName": "Service", - "method": "Method", + "method": "Method (deprecated)", + "methodName": "Method", "scheduledJob": "Scheduled Job", + "scheduledJobJob": "Scheduled Job Name", "data": "Data" }, "options": { diff --git a/application/Espo/Resources/layouts/Job/detailSmall.json b/application/Espo/Resources/layouts/Job/detailSmall.json index 69a2553345..06dd50e88c 100644 --- a/application/Espo/Resources/layouts/Job/detailSmall.json +++ b/application/Espo/Resources/layouts/Job/detailSmall.json @@ -13,7 +13,7 @@ "rows":[ [{"name":"scheduledJob"}, false], [{"name":"serviceName"}, false], - [{"name":"method"}, false], + [{"name":"methodName"}, false], [{"name": "data"}] ] } diff --git a/application/Espo/Resources/metadata/entityDefs/Job.json b/application/Espo/Resources/metadata/entityDefs/Job.json index e0657f129a..f0da2187b4 100644 --- a/application/Espo/Resources/metadata/entityDefs/Job.json +++ b/application/Espo/Resources/metadata/entityDefs/Job.json @@ -24,12 +24,21 @@ "required": true, "maxLength":100 }, + "methodName": { + "type": "varchar", + "maxLength": 100 + }, "data": { "type": "jsonObject" }, "scheduledJob": { "type": "link" }, + "scheduledJobJob": { + "type": "foreign", + "link": "scheduledJob", + "field": "job" + }, "attempts": { "type": "int" }, @@ -62,7 +71,7 @@ "collection": { "sortBy": "createdAt", "asc": false, - "textFilterFields": ["name", "method", "serviceName"] + "textFilterFields": ["name", "methodName", "serviceName", "scheduledJobName"] }, "indexes": { "executeTime": { diff --git a/application/Espo/Services/EmailNotification.php b/application/Espo/Services/EmailNotification.php index 10ac90805b..3c4744e607 100644 --- a/application/Espo/Services/EmailNotification.php +++ b/application/Espo/Services/EmailNotification.php @@ -94,15 +94,15 @@ class EmailNotification extends \Espo\Core\Services\Base public function notifyAboutAssignmentJob($data) { - if (empty($data['userId'])) return; - if (empty($data['assignerUserId'])) return; - if (empty($data['entityId'])) return; - if (empty($data['entityType'])) return; + if (empty($data->userId)) return; + if (empty($data->assignerUserId)) return; + if (empty($data->entityId)) return; + if (empty($data->entityType)) return; - $userId = $data['userId']; - $assignerUserId = $data['assignerUserId']; - $entityId = $data['entityId']; - $entityType = $data['entityType']; + $userId = $data->userId; + $assignerUserId = $data->assignerUserId; + $entityId = $data->entityId; + $entityType = $data->entityType; $user = $this->getEntityManager()->getEntity('User', $userId); diff --git a/application/Espo/Services/Import.php b/application/Espo/Services/Import.php index ce1576b557..0e5673f304 100644 --- a/application/Espo/Services/Import.php +++ b/application/Espo/Services/Import.php @@ -277,14 +277,14 @@ class Import extends \Espo\Services\Record public function runIdleImport($data) { - $entityType = $data['entityType']; + $entityType = $data->entityType; - $params = json_decode(json_encode($data['params']), true); + $params = json_decode(json_encode($data->params), true); - $importFieldList = $data['importFieldList']; - $attachmentId = $data['attachmentId']; + $importFieldList = $data->importFieldList; + $attachmentId = $data->attachmentId; - $importId = $data['importId']; + $importId = $data->importId; $this->import($entityType, $importFieldList, $attachmentId, $params, $importId); } @@ -332,7 +332,7 @@ class Import extends \Espo\Services\Record $job = $this->getEntityManager()->getEntity('Job'); $job->set(array( 'serviceName' => 'Import', - 'method' => 'runIdleImport', + 'methodName' => 'runIdleImport', 'data' => array( 'entityType' => $scope, 'params' => $params, diff --git a/application/Espo/Services/Stream.php b/application/Espo/Services/Stream.php index f3a1827f7a..dfe6a015c3 100644 --- a/application/Espo/Services/Stream.php +++ b/application/Espo/Services/Stream.php @@ -123,12 +123,12 @@ class Stream extends \Espo\Core\Services\Base if (empty($data)) { return; } - if (empty($data['entityId']) || empty($data['entityType']) || empty($data['userIdList'])) { + if (empty($data->entityId) || empty($data->entityType) || empty($data->userIdList)) { return; } - $userIdList = $data['userIdList']; - $entityType = $data['entityType']; - $entityId = $data['entityId']; + $userIdList = $data->userIdList; + $entityType = $data->entityType; + $entityId = $data->entityId; $entity = $this->getEntityManager()->getEntity($entityType, $entityId); if (!$entity) { @@ -1119,10 +1119,10 @@ class Stream extends \Espo\Core\Services\Base if (empty($data)) { return; } - if (empty($data['entityId']) || empty($data['entityType'])) { + if (empty($data->entityId) || empty($data->entityType)) { return; } - $entity = $this->getEntityManager()->getEntity($data['entityType'], $data['entityId']); + $entity = $this->getEntityManager()->getEntity($data->entityType, $data->entityId); if (!$entity) return; $idList = $this->getEntityFolowerIdList($entity); @@ -1144,7 +1144,5 @@ class Stream extends \Espo\Core\Services\Base } } } - } } - diff --git a/application/Espo/Services/User.php b/application/Espo/Services/User.php index 65474b887b..4e85d90137 100644 --- a/application/Espo/Services/User.php +++ b/application/Espo/Services/User.php @@ -178,11 +178,11 @@ class User extends Record $job->set(array( 'serviceName' => 'User', - 'method' => 'removeChangePasswordRequestJob', - 'data' => json_encode(array( - 'id' => $passwordChangeRequest->id, - )), - 'executeTime' => $dt->format('Y-m-d H:i:s') , + 'methodName' => 'removeChangePasswordRequestJob', + 'data' => [ + 'id' => $passwordChangeRequest->id + ], + 'executeTime' => $dt->format('Y-m-d H:i:s') )); $this->getEntityManager()->saveEntity($job); @@ -192,10 +192,10 @@ class User extends Record public function removeChangePasswordRequestJob($data) { - if (empty($data['id'])) { + if (empty($data->id)) { return; } - $id = $data['id']; + $id = $data->id; $p = $this->getEntityManager()->getEntity('PasswordChangeRequest', $id); if ($p) {