Skip to content

Commit

Permalink
Merge pull request #192 from ProcessMaker/feature/multiinstance
Browse files Browse the repository at this point in the history
Implement Activity MultiInstance. Resolves #191
  • Loading branch information
boliviacoca authored Feb 17, 2021
2 parents ee72116 + b120b46 commit eb9dfc9
Show file tree
Hide file tree
Showing 35 changed files with 3,202 additions and 15 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
}
},
"scripts": {
"test": "phpunit"
"test": "phpunit",
"coverage": "phpunit -d memory_limit=-1 --coverage-html coverage"
},
"require-dev": {
"phpunit/phpunit": "^5.7"
Expand Down
48 changes: 41 additions & 7 deletions src/ProcessMaker/Nayra/Bpmn/ActivityTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Exception;
use ProcessMaker\Nayra\Contracts\Bpmn\ActivityInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\FlowInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\LoopCharacteristicsInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\StateInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\TokenInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\TransitionInterface;
Expand Down Expand Up @@ -76,8 +77,11 @@ public function buildTransitions(RepositoryInterface $factory)
$this->exceptionTransition = new ExceptionTransition($this, true);
$this->closeExceptionTransition = new CloseExceptionTransition($this, true);
$this->completeExceptionTransition = new CompleteExceptionTransition($this, true);
$this->transition = new Transition($this, false);
$this->transition = new DataOutputTransition($this, false);
$this->closedState = new State($this, ActivityInterface::TOKEN_STATE_COMPLETED);
$this->loopTransition = new LoopCharacteristicsTransition($this, false);
$this->closedState->connectTo($this->loopTransition);
$this->loopTransition->connectTo($this->activeState);

$this->activeState->connectTo($this->exceptionTransition);
$this->activeState->connectTo($this->activityTransition);
Expand Down Expand Up @@ -116,6 +120,10 @@ function (TokenInterface $token) {
$this->closedState->attachEvent(
StateInterface::EVENT_TOKEN_ARRIVED,
function (TokenInterface $token) {
$loop = $this->getLoopCharacteristics();
if ($loop && $loop->isExecutable()) {
$loop->onTokenCompleted($token);
}
$this->getRepository()
->getTokenRepository()
->persistActivityCompleted($this, $token);
Expand All @@ -125,7 +133,11 @@ function (TokenInterface $token) {
$this->closeExceptionTransition->attachEvent(
TransitionInterface::EVENT_AFTER_CONSUME,
function ($transition, $tokens) {
$loop = $this->getLoopCharacteristics();
foreach ($tokens as $token) {
if ($loop && $loop->isExecutable()) {
$loop->onTokenTerminated($token);
}
$this->getRepository()
->getTokenRepository()
->persistActivityCompleted($this, $token);
Expand All @@ -136,12 +148,16 @@ function ($transition, $tokens) {
$this->closeActiveTransition->attachEvent(
TransitionInterface::EVENT_AFTER_CONSUME,
function ($transition, $tokens) {
$loop = $this->getLoopCharacteristics();
foreach ($tokens as $token) {
if ($loop && $loop->isExecutable()) {
$loop->onTokenTerminated($token);
}
$this->getRepository()
->getTokenRepository()
->persistActivityCompleted($this, $token);
$this->notifyEvent(ActivityInterface::EVENT_ACTIVITY_CANCELLED, $this, $transition, $tokens);
}
$this->notifyEvent(ActivityInterface::EVENT_ACTIVITY_CANCELLED, $this, $transition, $tokens);
}
);
}
Expand All @@ -156,7 +172,7 @@ function ($transition, $tokens) {
public function getInputPlace(FlowInterface $targetFlow = null)
{
$ready = new State($this, 'INCOMING');
$transition = new Transition($this, false);
$transition = new DataInputTransition($this, false);
$ready->connectTo($transition);
$transition->connectTo($this->activeState);
$this->addInput($ready);
Expand All @@ -181,11 +197,11 @@ protected function buildConnectionTo(FlowInterface $targetFlow)
function (TransitionInterface $transition, Collection $consumedTokens) {
foreach ($consumedTokens as $token) {
$token->setStatus(ActivityInterface::TOKEN_STATE_CLOSED);
$this->getRepository()
->getTokenRepository()
->persistActivityClosed($this, $token);
$this->notifyEvent(ActivityInterface::EVENT_ACTIVITY_CLOSED, $this, $token);
}
$this->getRepository()
->getTokenRepository()
->persistActivityClosed($this, $token);
$this->notifyEvent(ActivityInterface::EVENT_ACTIVITY_CLOSED, $this, $token);
}
);
return $this;
Expand Down Expand Up @@ -213,4 +229,22 @@ public function getActiveState()
{
return $this->activeState;
}

/**
* @return LoopCharacteristicsInterface
*/
public function getLoopCharacteristics()
{
return $this->getProperty(ActivityInterface::BPMN_PROPERTY_LOOP_CHARACTERISTICS);
}

/**
* @param LoopCharacteristicsInterface $loopCharacteristics
*
* @return static
*/
public function setLoopCharacteristics(LoopCharacteristicsInterface $loopCharacteristics)
{
return $this->setProperty(ActivityInterface::BPMN_PROPERTY_LOOP_CHARACTERISTICS, $loopCharacteristics);
}
}
66 changes: 66 additions & 0 deletions src/ProcessMaker/Nayra/Bpmn/DataInputTransition.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

namespace ProcessMaker\Nayra\Bpmn;

use ProcessMaker\Nayra\Contracts\Bpmn\ActivityInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\CollectionInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\ConnectionInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\StateInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\TokenInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\TransitionInterface;
use ProcessMaker\Nayra\Contracts\Engine\ExecutionInstanceInterface;

/**
* Transition rule that always pass the token.
*
* @package ProcessMaker\Nayra\Bpmn
*/
class DataInputTransition implements TransitionInterface
{
use TransitionTrait;

/**
* Condition required to transit the element.
*
* @param \ProcessMaker\Nayra\Contracts\Bpmn\TokenInterface|null $token
* @param \ProcessMaker\Nayra\Contracts\Engine\ExecutionInstanceInterface|null $executionInstance
*
* @return bool
*/
public function assertCondition(TokenInterface $token = null, ExecutionInstanceInterface $executionInstance = null)
{
return true;
}

/**
* Get transition owner element
*
* @return ActivityInterface
*/
public function getOwner()
{
return $this->owner;
}

/**
* Activate the next state.
*
* @param \ProcessMaker\Nayra\Contracts\Bpmn\ConnectionInterface $flow
* @param \ProcessMaker\Nayra\Contracts\Engine\ExecutionInstanceInterface $instance
* @param \ProcessMaker\Nayra\Contracts\Bpmn\CollectionInterface $consumeTokens
* @param array $properties
* @param \ProcessMaker\Nayra\Contracts\Bpmn\TransitionInterface|null $source
*
* @return TokenInterface
*/
protected function activateNextState(ConnectionInterface $flow, ExecutionInstanceInterface $instance, CollectionInterface $consumeTokens, array $properties = [], TransitionInterface $source = null)
{
$nextState = $flow->targetState();
$loop = $this->getOwner()->getLoopCharacteristics();
if ($loop && $loop->isExecutable()) {
$loop->iterateNextState($nextState, $instance, $consumeTokens, $properties, $source);
} else {
$nextState->addNewToken($instance, $properties, $source);
}
}
}
76 changes: 76 additions & 0 deletions src/ProcessMaker/Nayra/Bpmn/DataOutputTransition.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

namespace ProcessMaker\Nayra\Bpmn;

use ProcessMaker\Nayra\Contracts\Bpmn\ActivityInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\CollectionInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\ConnectionInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\TokenInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\TransitionInterface;
use ProcessMaker\Nayra\Contracts\Engine\ExecutionInstanceInterface;

/**
* Transition rule that always pass the token.
*
* @package ProcessMaker\Nayra\Bpmn
*/
class DataOutputTransition implements TransitionInterface
{
use TransitionTrait;

/**
* Initialize the transition.
*
* @param FlowNodeInterface $owner
* @param bool $preserveToken
*/
protected function initDataOutputTransition()
{
$this->setTokensConsumedPerIncoming(-1);
}

/**
* Condition required to transit the element.
*
* @param \ProcessMaker\Nayra\Contracts\Bpmn\TokenInterface|null $token
* @param \ProcessMaker\Nayra\Contracts\Engine\ExecutionInstanceInterface|null $executionInstance
*
* @return bool
*/
public function assertCondition(TokenInterface $token = null, ExecutionInstanceInterface $executionInstance = null)
{
$loop = $this->getOwner()->getLoopCharacteristics();
return !$loop || !$loop->isExecutable() || $loop->isLoopCompleted($executionInstance, $token);
}

/**
* Get transition owner element
*
* @return ActivityInterface
*/
public function getOwner()
{
return $this->owner;
}

/**
* Activate the next state.
*
* @param \ProcessMaker\Nayra\Contracts\Bpmn\ConnectionInterface $flow
* @param \ProcessMaker\Nayra\Contracts\Engine\ExecutionInstanceInterface $instance
* @param \ProcessMaker\Nayra\Contracts\Bpmn\CollectionInterface $consumeTokens
* @param array $properties
* @param \ProcessMaker\Nayra\Contracts\Bpmn\TransitionInterface|null $source
*
* @return TokenInterface
*/
protected function activateNextState(ConnectionInterface $flow, ExecutionInstanceInterface $instance, CollectionInterface $consumeTokens, array $properties = [], TransitionInterface $source = null)
{
$loop = $this->getOwner()->getLoopCharacteristics();
if ($loop && $loop->isExecutable()) {
$loop->mergeOutputData($consumeTokens, $instance);
}
$nextState = $flow->targetState();
$nextState->addNewToken($instance, $properties, $source);
}
}
81 changes: 81 additions & 0 deletions src/ProcessMaker/Nayra/Bpmn/LoopCharacteristicsTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

namespace ProcessMaker\Nayra\Bpmn;

use ProcessMaker\Nayra\Contracts\Bpmn\LoopCharacteristicsInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\TokenInterface;

/**
* Base implementation for LoopCharacteristicsInterface
*
* @package ProcessMaker\Nayra\Bpmn
*/
trait LoopCharacteristicsTrait
{
use BaseTrait;

/**
* Prepare Loop Instance properties for execution
*
* @param TokenInterface $token
* @param array $properties
*
* @return array
*/
private function prepareLoopInstanceProperties(TokenInterface $token, array $properties = [])
{
$loopCharacteristics = $token->getProperty(
LoopCharacteristicsInterface::BPMN_LOOP_INSTANCE_PROPERTY,
$properties[LoopCharacteristicsInterface::BPMN_LOOP_INSTANCE_PROPERTY] ?? []
);
if (empty($loopCharacteristics['sourceToken'])) {
$loopCharacteristics['sourceToken'] = $token->getId();
}
$properties[LoopCharacteristicsInterface::BPMN_LOOP_INSTANCE_PROPERTY] = $loopCharacteristics;
$token->setProperty(
LoopCharacteristicsInterface::BPMN_LOOP_INSTANCE_PROPERTY,
$properties[LoopCharacteristicsInterface::BPMN_LOOP_INSTANCE_PROPERTY]
);
return $properties;
}

/**
* Set Loop Instance property during execution
*
* @param TokenInterface $token
* @param string $key
* @param mixed $value
*
* @return self
*/
private function setLoopInstanceProperty(TokenInterface $token, $key, $value)
{
$loopCharacteristics = $token->getProperty(LoopCharacteristicsInterface::BPMN_LOOP_INSTANCE_PROPERTY, []);
$outerInstance = $loopCharacteristics['sourceToken'];
$ds = $token->getInstance()->getDataStore();
$data = $ds->getData(LoopCharacteristicsInterface::BPMN_LOOP_INSTANCE_PROPERTY, []);
$data[$outerInstance] = $data[$outerInstance] ?? [];
$data[$outerInstance][$key] = $value;
$ds->putData(LoopCharacteristicsInterface::BPMN_LOOP_INSTANCE_PROPERTY, $data);
return $this;
}

/**
* Get Loop Instance property during execution
*
* @param TokenInterface $token
* @param string $key
* @param mixed $defaultValue
*
* @return mixed
*/
private function getLoopInstanceProperty(TokenInterface $token, $key, $defaultValue = null)
{
$loopCharacteristics = $token->getProperty(LoopCharacteristicsInterface::BPMN_LOOP_INSTANCE_PROPERTY, []);
$outerInstance = $loopCharacteristics['sourceToken'];
$ds = $token->getInstance()->getDataStore();
$data = $ds->getData(LoopCharacteristicsInterface::BPMN_LOOP_INSTANCE_PROPERTY, []);
$data[$outerInstance] = $data[$outerInstance] ?? [];
return $data[$outerInstance][$key] ?? $defaultValue;
}
}
Loading

0 comments on commit eb9dfc9

Please sign in to comment.