Esempio n. 1
0
 /**
  * {@inheritdoc}
  */
 public function executeJob(Job $job)
 {
     $stmt = $this->engine->prepareQuery("\n\t\t\tUPDATE `#__bpmn_job`\n\t\t\tSET `lock_owner` = :owner,\n\t\t\t\t`locked_at` = :time\n\t\t\tWHERE `id` = :id\n\t\t\tAND (`lock_owner` IS NULL OR `locked_at` < :expires)\n\t\t");
     $stmt->bindValue('owner', $this->lockOwner);
     $stmt->bindValue('time', time());
     $stmt->bindValue('expires', time() - $this->lockTimeout);
     $stmt->bindValue('id', $job->getId());
     $locked = $stmt->execute();
     if ($locked < 1) {
         $jobs = $this->engine->getManagementService()->createJobQuery()->jobId($job->getId())->findAll();
         if (empty($jobs)) {
             $this->engine->info('Unable to lock job <{job}> because it does not exist in the DB', ['job' => (string) $job->getId()]);
             return;
         }
         $this->engine->info('Unable to lock job <{job}> because it is locked by job executor "{executor}"', ['job' => (string) $job->getId(), 'executor' => $job->getLockOwner()]);
         return;
     }
     $this->engine->executeCommand(new ExecuteJobCommand($job, $this->findJobHandler($job)));
     $jobs = $this->engine->getManagementService()->createJobQuery()->jobId($job->getId())->findAll();
     if (!empty($jobs)) {
         $job = array_pop($jobs);
         if ($job->getRetries() > 0) {
             $this->engine->info('Re-scheduling job <{job}>, {retries} retries left', ['job' => (string) $job->getId(), 'retries' => $job->getRetries()]);
             $this->scheduledJobs[] = $job;
         }
     }
 }
 /**
  * {@inheritdoc}
  */
 public function executeCommand(ProcessEngine $engine)
 {
     $sql = "\tSELECT s.`id`, s.`execution_id`, s.`activity_id`, s.`node`\r\n\t\t\t\t\tFROM `#__bpmn_event_subscription` AS s\r\n\t\t\t\t\tINNER JOIN `#__bpmn_execution` AS e ON (e.`id` = s.`execution_id`)\r\n\t\t\t\t\tWHERE s.`name` = :message\r\n\t\t\t\t\tAND s.`flags` = :flags\r\n\t\t\t\t\tAND s.`execution_id` = :eid\r\n\t\t\t\t\tORDER BY e.`depth` DESC, s.`created_at`\r\n\t\t";
     $stmt = $engine->prepareQuery($sql);
     $stmt->bindValue('message', $this->messageName);
     $stmt->bindValue('flags', EventSubscription::TYPE_MESSAGE);
     $stmt->bindValue('eid', $this->executionId);
     $stmt->setLimit(1);
     $stmt->execute();
     $row = $stmt->fetchNextRow();
     if ($row === false) {
         throw new \RuntimeException(sprintf('Execution %s has not subscribed to message %s', $this->executionId, $this->messageName));
     }
     $execution = $engine->findExecution($this->executionId);
     $delegation = [];
     if ($row['node'] !== NULL) {
         $delegation['nodeId'] = $row['node'];
     }
     // Delete timer jobs:
     $stmt = $engine->prepareQuery("\r\n\t\t\tSELECT `job_id`\r\n\t\t\tFROM `#__bpmn_event_subscription`\r\n\t\t\tWHERE `execution_id` = :eid\r\n\t\t\tAND `activity_id` = :aid\r\n\t\t\tAND `flags` = :flags\r\n\t\t\tAND `job_id` IS NOT NULL\r\n\t\t");
     $stmt->bindValue('eid', $execution->getId());
     $stmt->bindValue('aid', $row['activity_id']);
     $stmt->bindValue('flags', EventSubscription::TYPE_TIMER);
     $stmt->transform('job_id', new UUIDTransformer());
     $stmt->execute();
     $management = $engine->getManagementService();
     foreach ($stmt->fetchColumns('job_id') as $jobId) {
         $management->removeJob($jobId);
     }
     $sql = "\tDELETE FROM `#__bpmn_event_subscription`\r\n\t\t\t\t\tWHERE `execution_id` = :eid\r\n\t\t\t\t\tAND `activity_id` = :aid\r\n\t\t";
     $stmt = $engine->prepareQuery($sql);
     $stmt->bindValue('eid', $execution->getId());
     $stmt->bindValue('aid', $row['activity_id']);
     $count = $stmt->execute();
     $message = sprintf('Cleared {count} event subscription%s related to activity <{activity}> within {execution}', $count == 1 ? '' : 's');
     $engine->debug($message, ['count' => $count, 'activity' => $row['activity_id'], 'execution' => (string) $execution]);
     $execution->signal($this->messageName, unserialize($this->variables), $delegation);
 }
Esempio n. 3
0
 protected function setUp()
 {
     parent::setUp();
     $this->eventDispatcher = new EventDispatcher();
     $this->conn = static::createConnection('bpm_', $this->eventDispatcher);
     static::migrateDirectoryUp($this->conn, __DIR__ . '/../../migration');
     $logger = NULL;
     if (NULL !== ($logLevel = $this->getLogLevel())) {
         $stderr = fopen('php://stderr', 'wb');
         $levels = array_change_key_case(Logger::getLevels(), CASE_UPPER);
         $logger = new Logger('BPMN');
         $logger->pushHandler(new StreamHandler($stderr, $levels[strtoupper($logLevel)]));
         $logger->pushProcessor(new PsrLogMessageProcessor());
         fwrite($stderr, "\n");
         fwrite($stderr, sprintf("TEST CASE: %s\n", $this->getName()));
         // 			$this->conn$conn->setDebug(true);
         // 			$this->conn->setLogger($logger);
     }
     $this->messageHandlers = [];
     $this->serviceTaskHandlers = [];
     // Provide message handler subscriptions.
     $this->eventDispatcher->connect(function (MessageThrownEvent $event) {
         $def = $this->repositoryService->createProcessDefinitionQuery()->processDefinitionId($event->execution->getProcessDefinitionId())->findOne();
         $key = $def->getKey();
         $id = $event->execution->getActivityId();
         if (isset($this->messageHandlers[$key][$id])) {
             return $this->messageHandlers[$key][$id]($event);
         }
     });
     $this->eventDispatcher->connect(function (TaskExecutedEvent $event) {
         $execution = $this->runtimeService->createExecutionQuery()->executionId($event->execution->getExecutionId())->findOne();
         $key = $execution->getProcessDefinition()->getKey();
         $id = $event->execution->getActivityId();
         if (isset($this->serviceTaskHandlers[$key][$id])) {
             $this->serviceTaskHandlers[$key][$id]($event->execution);
         }
     });
     // Allow for assertions in expressions, e.g. #{ @test.assertEquals(2, processVariable) }
     $this->eventDispatcher->connect(function (CreateExpressionContextEvent $event) {
         $event->access->setVariable('@test', $this);
     });
     $this->delegateTasks = new DelegateTaskRegistry();
     $this->processEngine = new ProcessEngine($this->conn, $this->eventDispatcher, new ExpressionContextFactory());
     $this->processEngine->setDelegateTaskFactory($this->delegateTasks);
     $this->processEngine->setLogger($logger);
     $scheduler = new TestJobScheduler($this->processEngine);
     $this->jobExecutor = new JobExecutor($this->processEngine, $scheduler);
     $this->processEngine->setJobExecutor($this->jobExecutor);
     $this->repositoryService = $this->processEngine->getRepositoryService();
     $this->runtimeService = $this->processEngine->getRuntimeService();
     $this->taskService = $this->processEngine->getTaskService();
     $this->historyService = $this->processEngine->getHistoryService();
     $this->managementService = $this->processEngine->getManagementService();
     if ($this->typeInfo === NULL) {
         $this->typeInfo = new ReflectionTypeInfo(new \ReflectionClass(get_class($this)));
     }
     foreach ($this->typeInfo->getMethods() as $method) {
         if (!$method->isPublic() || $method->isStatic()) {
             continue;
         }
         foreach ($method->getAnnotations() as $anno) {
             if ($anno instanceof MessageHandler) {
                 $this->messageHandlers[$anno->processKey][$anno->value] = [$this, $method->getName()];
             }
             if ($anno instanceof ServiceTaskHandler) {
                 $this->serviceTaskHandlers[$anno->processKey][$anno->value] = [$this, $method->getName()];
             }
         }
     }
 }
 /**
  * {@inheritdoc}
  */
 public function executeCommand(ProcessEngine $engine)
 {
     $sql = "\tSELECT s.`id`, s.`execution_id`, s.`activity_id`, s.`node`\r\n\t\t\t\t\tFROM `#__bpmn_event_subscription` AS s\r\n\t\t\t\t\tINNER JOIN `#__bpmn_execution` AS e ON (e.`id` = s.`execution_id`)\r\n\t\t\t\t\tWHERE s.`name` = :signal\r\n\t\t\t\t\tAND s.`flags` = :flags\r\n\t\t\t\t\tORDER BY e.`depth` DESC\r\n\t\t";
     if ($this->executionId !== NULL) {
         $sql .= ' AND s.`execution_id` = :eid';
     }
     $stmt = $engine->prepareQuery($sql);
     $stmt->bindValue('signal', $this->signal);
     $stmt->bindValue('flags', EventSubscription::TYPE_SIGNAL);
     if ($this->executionId !== NULL) {
         $stmt->bindValue('eid', $this->executionId);
     }
     $stmt->transform('execution_id', new UUIDTransformer());
     $stmt->execute();
     $ids = [];
     $executions = [];
     $delegations = [];
     foreach ($stmt->fetchRows() as $row) {
         $execution = $executions[] = $engine->findExecution($row['execution_id']);
         $ids[(string) $execution->getId()] = [$execution->getId(), $row['activity_id']];
         if ($row['node'] !== NULL) {
             $delegations[(string) $execution->getId()] = ['nodeId' => $row['node']];
         }
     }
     if (!empty($ids)) {
         $sql = "SELECT `job_id` FROM `#__bpmn_event_subscription` WHERE `flags` = :flags AND `job_id` IS NOT NULL AND (";
         $where = [];
         $params = ['flags' => EventSubscription::TYPE_TIMER];
         foreach (array_values($ids) as $i => $tmp) {
             $where[] = sprintf("(`execution_id` = :e%u AND `activity_id` = :a%u)", $i, $i);
             $params['e' . $i] = $tmp[0];
             $params['a' . $i] = $tmp[1];
         }
         $stmt = $engine->prepareQuery($sql . implode(' OR ', $where) . ')');
         $stmt->bindAll($params);
         $stmt->transform('job_id', new UUIDTransformer());
         $stmt->execute();
         $management = $engine->getManagementService();
         foreach ($stmt->fetchColumns('job_id') as $jobId) {
             $management->removeJob($jobId);
         }
         unset($params['flags']);
         $stmt = $engine->prepareQuery("DELETE FROM `#__bpmn_event_subscription` WHERE " . implode(' OR ', $where));
         $stmt->bindAll($params);
         $count = $stmt->execute();
         $message = sprintf('Cleared {count} event subscription%s related to signal <{signal}>', $count == 1 ? '' : 's');
         $engine->debug($message, ['count' => $count, 'signal' => $this->signal === NULL ? 'NULL' : $this->signal]);
     }
     $vars = unserialize($this->variables);
     foreach ($executions as $execution) {
         $id = (string) $execution->getId();
         $execution->signal($this->signal, $vars, empty($delegations[$id]) ? [] : $delegations[$id]);
     }
     // Include signal start events subscriptions.
     $sql = "\tSELECT s.`name` AS signal_name, d.* \r\n\t\t\t\t\tFROM `#__bpmn_process_subscription` AS s\r\n\t\t\t\t\tINNER JOIN `#__bpmn_process_definition` AS d ON (d.`id` = s.`definition_id`)\r\n\t\t\t\t\tWHERE s.`flags` = :flags\r\n\t\t\t\t\tAND s.`name` = :name\r\n\t\t";
     $stmt = $engine->prepareQuery($sql);
     $stmt->bindValue('flags', EventSubscription::TYPE_SIGNAL);
     $stmt->bindValue('name', $this->signal);
     $stmt->transform('id', new UUIDTransformer());
     $stmt->transform('deployment_id', new UUIDTransformer());
     $stmt->execute();
     $source = $this->sourceExecutionId === NULL ? NULL : $engine->findExecution($this->sourceExecutionId);
     while ($row = $stmt->fetchNextRow()) {
         $definition = new ProcessDefinition($row['id'], $row['process_key'], $row['revision'], unserialize(BinaryData::decode($row['definition'])), $row['name'], new \DateTimeImmutable('@' . $row['deployed_at']), $row['deployment_id']);
         $engine->pushCommand(new StartProcessInstanceCommand($definition, $definition->findSignalStartEvent($row['signal_name']), $source === NULL ? NULL : $source->getBusinessKey(), $vars));
     }
     if ($source !== NULL) {
         $source->signal();
     }
 }