Author: sb Date: Mon Jan 21 17:04:16 2008 New Revision: 7215 Log: - Implement the Cancel Case workflow pattern.
Added: trunk/Workflow/src/interfaces/rollbackable_service_object.php - copied, changed from r7197, trunk/Workflow/src/interfaces/service_object.php trunk/Workflow/src/nodes/cancel.php - copied, changed from r7197, trunk/Workflow/src/nodes/end.php trunk/Workflow/tests/data/ParallelSplitActionActionCancelCaseSynchronization.dot (with props) trunk/Workflow/tests/data/ParallelSplitActionActionCancelCaseSynchronization_1.xml (with props) trunk/Workflow/tests/data/ParallelSplitCancelCaseActionActionSynchronization.dot (with props) trunk/Workflow/tests/data/ParallelSplitCancelCaseActionActionSynchronization_1.xml (with props) trunk/WorkflowEventLogTiein/tests/data/ParallelSplitActionActionCancelCaseSynchronization.log (with props) trunk/WorkflowEventLogTiein/tests/data/ParallelSplitCancelCaseActionActionSynchronization.log (with props) trunk/WorkflowEventLogTiein/tests/data/ParallelSplitSynchronizationCancelCase.log (with props) Modified: trunk/Workflow/ChangeLog trunk/Workflow/src/interfaces/execution.php trunk/Workflow/src/nodes/action.php trunk/Workflow/src/workflow_autoload.php trunk/Workflow/tests/case.php trunk/Workflow/tests/definition_xml_test.php trunk/Workflow/tests/execution_test.php trunk/Workflow/tests/visitor_visualization_test.php trunk/WorkflowDatabaseTiein/ChangeLog trunk/WorkflowDatabaseTiein/src/execution.php trunk/WorkflowEventLogTiein/tests/listener_test.php Modified: trunk/Workflow/ChangeLog ============================================================================== --- trunk/Workflow/ChangeLog [iso-8859-1] (original) +++ trunk/Workflow/ChangeLog [iso-8859-1] Mon Jan 21 17:04:16 2008 @@ -1,3 +1,5 @@ +- Implemented issue #10941: Support for the Cancel Case workflow pattern. + - Implemented issue #12404: Separate file i/o from XML processing in ezcWorkflowDefinitionStorageXml. Modified: trunk/Workflow/src/interfaces/execution.php ============================================================================== --- trunk/Workflow/src/interfaces/execution.php [iso-8859-1] (original) +++ trunk/Workflow/src/interfaces/execution.php [iso-8859-1] Mon Jan 21 17:04:16 2008 @@ -69,6 +69,14 @@ protected $numActivatedEndNodes = 0; /** + * Rollbackable service objects that have been executed + * in the current execution loop. + * + * @var array + */ + protected $rollbackableServiceObjects = array(); + + /** * Nodes of the workflow that started a new thread of execution. * * @var array @@ -81,6 +89,11 @@ * @var integer */ protected $nextThreadId = 0; + + /** + * @var bool + */ + protected $cancelled; /** * @var bool @@ -221,6 +234,7 @@ ); } + $this->cancelled = false; $this->ended = false; $this->resumed = false; $this->suspended = false; @@ -266,6 +280,7 @@ */ public function suspend() { + $this->cancelled = false; $this->ended = false; $this->resumed = false; $this->suspended = true; @@ -318,6 +333,7 @@ ); } + $this->cancelled = false; $this->ended = false; $this->resumed = true; $this->suspended = false; @@ -370,6 +386,41 @@ } /** + * Cancels workflow execution with the node $endNode. + * + * @param ezcWorkflowNodeEnd $endNode + * @ignore + */ + public function cancel( ezcWorkflowNodeEnd $endNode ) + { + $this->activatedNodes = array(); + $this->cancelled = true; + $this->numActivatedNodes = 0; + + foreach ( $this->rollbackableServiceObjects as $object ) + { + $result = $object['object']->rollback( $this ); + + $this->notifyListeners( + sprintf( + '%s back service object "%s" of node #%d for instance #%d ' . + 'of workflow "%s" (version %d).', + + $result ? 'Rolled' : 'Could not roll', + get_class( $object['object'] ), + $object['node']->getId(), + $this->id, + $this->workflow->name, + $this->workflow->version + ), + ezcWorkflowExecutionListener::DEBUG + ); + } + + $this->end( $endNode ); + } + + /** * Ends workflow execution with the node $endNode. * * End nodes must call this method to end the execution. @@ -400,12 +451,16 @@ ezcWorkflowExecutionListener::DEBUG ); - $this->endThread( $endNode->getThreadId() ); + if ( !$this->cancelled ) + { + $this->endThread( $endNode->getThreadId() ); + } $this->notifyListeners( sprintf( - 'Ended execution #%d of workflow "%s" (version %d).', - + '%s execution #%d of workflow "%s" (version %d).', + + $this->cancelled ? 'Cancelled' : 'Ended', $this->id, $this->workflow->name, $this->workflow->version @@ -421,8 +476,7 @@ */ protected function execute() { - // Try to execute nodes until while there are executable nodes on the - // stack of activated nodes. + // Try to execute nodes while there are executable nodes on the stack. do { // Flag that indicates whether a node has been executed during the @@ -438,7 +492,8 @@ { // The current node is an end node but there are still // activated nodes on the stack. - if ( $node instanceof ezcWorkflowNodeEnd && + if ( $node instanceof ezcWorkflowNodeEnd && + !$node instanceof ezcWorkflowNodeCancel && $this->numActivatedNodes != $this->numActivatedEndNodes ) { continue; @@ -543,6 +598,20 @@ /** * Adds a variable that an (input) node is waiting for. * + * @param ezcWorkflowNodeAction $node + * @param ezcWorkflowRollbackableServiceObject $object + * @ignore + */ + public function addRollbackableServiceObject( ezcWorkflowNodeAction $node, ezcWorkflowRollbackableServiceObject $object ) + { + $this->rollbackableServiceObjects[] = array( + 'node' => $node, 'object' => $object + ); + } + + /** + * Adds a variable that an (input) node is waiting for. + * * @param ezcWorkflowNode $node * @param string $variableName * @param ezcWorkflowCondition $condition @@ -888,6 +957,16 @@ } /** + * Returns true when the workflow execution has been cancelled. + * + * @return bool + */ + public function isCancelled() + { + return $this->cancelled; + } + + /** * Returns true when the workflow execution has ended. * * @return bool Copied: trunk/Workflow/src/interfaces/rollbackable_service_object.php (from r7197, trunk/Workflow/src/interfaces/service_object.php) ============================================================================== --- trunk/Workflow/src/interfaces/service_object.php [iso-8859-1] (original) +++ trunk/Workflow/src/interfaces/rollbackable_service_object.php [iso-8859-1] Mon Jan 21 17:04:16 2008 @@ -1,6 +1,6 @@ <?php /** - * File containing the ezcWorkflowServiceObject interface. + * File containing the ezcWorkflowRollbackableServiceObject interface. * * @package Workflow * @version //autogen// @@ -9,34 +9,23 @@ */ /** - * Interface for service objects that can be attached to - * ezcWorkflowNodeAction nodes. + * Interface for service objects for which the action can be rolled back + * in case the workflow execution is cancelled. * * @package Workflow * @version //autogen// */ -interface ezcWorkflowServiceObject +interface ezcWorkflowRollbackableServiceObject extends ezcWorkflowServiceObject { /** - * Executes the business logic of this service object. + * Rolls back the business logic of this service object. * - * Implementations can return true if the execution of the - * service object was successful to resume the workflow and activate - * the next node. - * - * Returning false will cause the workflow to be suspended and the service - * object to be executed again on a later invokation. + * Implementors should return true when the rollback was successful + * and false when it was not. * * @param ezcWorkflowExecution $execution * @return boolean */ - public function execute( ezcWorkflowExecution $execution ); - - /** - * Returns a textual representation of this service object. - * - * @return string - */ - public function __toString(); + public function rollback( ezcWorkflowExecution $execution ); } ?> Modified: trunk/Workflow/src/nodes/action.php ============================================================================== --- trunk/Workflow/src/nodes/action.php [iso-8859-1] (original) +++ trunk/Workflow/src/nodes/action.php [iso-8859-1] Mon Jan 21 17:04:16 2008 @@ -107,7 +107,13 @@ */ public function execute( ezcWorkflowExecution $execution ) { - $object = $this->createObject(); + $object = $this->createObject(); + + if ( $object instanceof ezcWorkflowRollbackableServiceObject ) + { + $execution->addRollbackableServiceObject( $this, $object ); + } + $finished = $object->execute( $execution ); // Execution of the Service Object has finished. Copied: trunk/Workflow/src/nodes/cancel.php (from r7197, trunk/Workflow/src/nodes/end.php) ============================================================================== --- trunk/Workflow/src/nodes/end.php [iso-8859-1] (original) +++ trunk/Workflow/src/nodes/cancel.php [iso-8859-1] Mon Jan 21 17:04:16 2008 @@ -1,6 +1,6 @@ <?php /** - * File containing the ezcWorkflowNodeEnd class. + * File containing the ezcWorkflowNodeCancel class. * * @package Workflow * @version //autogen// @@ -9,57 +9,45 @@ */ /** - * An object of the ezcWorkflowNodeEnd class represents an end node of a workflow. + * This node implements the Cancel Case workflow pattern. * - * A workflow must have at least one end node. The execution of the workflow ends - * when an end node is reached. - * Creating an object of the ezcWorkflow class automatically creates a default end node for the new - * workflow. It can be accessed through the getEndNode() method. + * A complete process instance is removed. This includes currently executing + * tasks, those which may execute at some future time and all sub-processes. + * The process instance is recorded as having completed unsuccessfully. * * Incoming nodes: 1 - * Outgoing nodes: 0 - * - * Example: - * <code> - * $workflow = new ezcWorkflow( 'Test' ); - * // build up your workflow here... result in $node - * $node = .... - * $workflow->startNode->addOutNode( ....some other node here .. ); - * $node->addOutNode( $workflow->endNode ); - * </code> + * Outgoing nodes: 0..1 * * @package Workflow * @version //autogen// */ -class ezcWorkflowNodeEnd extends ezcWorkflowNode +class ezcWorkflowNodeCancel extends ezcWorkflowNodeEnd { /** * Constraint: The minimum number of outgoing nodes this node has to have - * to be valid. + * to be valid. Set to false to disable this constraint. * * @var integer */ - protected $minOutNodes = 0; + protected $minOutNodes = false; /** * Constraint: The maximum number of outgoing nodes this node has to have - * to be valid. + * to be valid. Set to false to disable this constraint. * * @var integer */ - protected $maxOutNodes = 0; + protected $maxOutNodes = false; /** - * Ends the execution of this workflow. + * Cancels the execution of this workflow. * * @param ezcWorkflowExecution $execution * @ignore */ public function execute( ezcWorkflowExecution $execution ) { - $execution->end( $this ); - - return parent::execute( $execution ); + $execution->cancel( $this ); } } ?> Modified: trunk/Workflow/src/workflow_autoload.php ============================================================================== --- trunk/Workflow/src/workflow_autoload.php [iso-8859-1] (original) +++ trunk/Workflow/src/workflow_autoload.php [iso-8859-1] Mon Jan 21 17:04:16 2008 @@ -57,6 +57,7 @@ 'ezcWorkflowNodeAction' => 'Workflow/nodes/action.php', 'ezcWorkflowNodeDiscriminator' => 'Workflow/nodes/control_flow/discriminator.php', 'ezcWorkflowNodeEnd' => 'Workflow/nodes/end.php', + 'ezcWorkflowNodeCancel' => 'Workflow/nodes/cancel.php', 'ezcWorkflowNodeExclusiveChoice' => 'Workflow/nodes/control_flow/exclusive_choice.php', 'ezcWorkflowNodeInput' => 'Workflow/nodes/variables/input.php', 'ezcWorkflowNodeLoop' => 'Workflow/nodes/control_flow/loop.php', @@ -75,6 +76,7 @@ 'ezcWorkflowNodeVariableSub' => 'Workflow/nodes/variables/sub.php', 'ezcWorkflowNodeVariableUnset' => 'Workflow/nodes/variables/unset.php', 'ezcWorkflowServiceObject' => 'Workflow/interfaces/service_object.php', + 'ezcWorkflowRollbackableServiceObject' => 'Workflow/interfaces/rollbackable_service_object.php', 'ezcWorkflowUtil' => 'Workflow/util.php', 'ezcWorkflowVariableHandler' => 'Workflow/interfaces/variable_handler.php', 'ezcWorkflowVisitorNodeCollector' => 'Workflow/visitors/node_collector.php', Modified: trunk/Workflow/tests/case.php ============================================================================== --- trunk/Workflow/tests/case.php [iso-8859-1] (original) +++ trunk/Workflow/tests/case.php [iso-8859-1] Mon Jan 21 17:04:16 2008 @@ -33,7 +33,7 @@ if ( !class_exists( 'ServiceObject', false ) ) { - $this->getMock( 'ezcWorkflowServiceObject', array(), array(), 'ServiceObject' ); + $this->getMock( 'ezcWorkflowRollbackableServiceObject', array(), array(), 'ServiceObject' ); } } @@ -690,6 +690,51 @@ ->addConditionalOutNode( $outerBreak, $this->endNode ); } + protected function setUpCancelCase( $order ) + { + if ( $order == 'first' ) + { + $workflowName = 'ParallelSplitCancelCaseActionActionSynchronization'; + } + else + { + $workflowName = 'ParallelSplitActionActionCancelCaseSynchronization'; + } + + $this->workflow = new ezcWorkflow( $workflowName ); + $this->setUpReferences(); + + $this->branchNode = new ezcWorkflowNodeParallelSplit; + $cancelNode = new ezcWorkflowNodeCancel; + $actionNodeA = new ezcWorkflowNodeAction( 'ServiceObject' ); + $actionNodeB = new ezcWorkflowNodeAction( 'ServiceObject' ); + $synchronization = new ezcWorkflowNodeSynchronization; + + if ( $order == 'first' ) + { + $this->branchNode->addOutNode( $cancelNode ); + $this->branchNode->addOutNode( $actionNodeA ); + $this->branchNode->addOutNode( $actionNodeB ); + + $synchronization->addInNode( $cancelNode ); + $synchronization->addInNode( $actionNodeA ); + $synchronization->addInNode( $actionNodeB ); + } + else + { + $this->branchNode->addOutNode( $actionNodeA ); + $this->branchNode->addOutNode( $actionNodeB ); + $this->branchNode->addOutNode( $cancelNode ); + + $synchronization->addInNode( $actionNodeA ); + $synchronization->addInNode( $actionNodeB ); + $synchronization->addInNode( $cancelNode ); + } + + $this->startNode->addOutNode( $this->branchNode ); + $this->endNode->addInNode( $synchronization ); + } + protected function setUpReferences() { $this->startNode = $this->workflow->startNode; Added: trunk/Workflow/tests/data/ParallelSplitActionActionCancelCaseSynchronization.dot ============================================================================== --- trunk/Workflow/tests/data/ParallelSplitActionActionCancelCaseSynchronization.dot (added) +++ trunk/Workflow/tests/data/ParallelSplitActionActionCancelCaseSynchronization.dot [iso-8859-1] Mon Jan 21 17:04:16 2008 @@ -1,0 +1,18 @@ +digraph ParallelSplitActionActionCancelCaseSynchronization { +node1 [label="Start"] +node2 [label="Parallel Split"] +node3 [label=""] +node4 [label="Synchronization"] +node5 [label="End"] +node6 [label=""] +node7 [label="Cancel"] + +node1 -> node2 +node2 -> node3 +node2 -> node6 +node2 -> node7 +node3 -> node4 +node4 -> node5 +node6 -> node4 +node7 -> node4 +} Propchange: trunk/Workflow/tests/data/ParallelSplitActionActionCancelCaseSynchronization.dot ------------------------------------------------------------------------------ svn:eol-style = native Added: trunk/Workflow/tests/data/ParallelSplitActionActionCancelCaseSynchronization_1.xml ============================================================================== --- trunk/Workflow/tests/data/ParallelSplitActionActionCancelCaseSynchronization_1.xml (added) +++ trunk/Workflow/tests/data/ParallelSplitActionActionCancelCaseSynchronization_1.xml [iso-8859-1] Mon Jan 21 17:04:16 2008 @@ -1,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<workflow name="ParallelSplitActionActionCancelCaseSynchronization" version="1"> + <node id="1" type="Start"> + <outNode id="2"/> + </node> + <node id="2" type="ParallelSplit"> + <outNode id="3"/> + <outNode id="6"/> + <outNode id="7"/> + </node> + <node id="3" type="Action" serviceObjectClass="ServiceObject"> + <outNode id="4"/> + </node> + <node id="4" type="Synchronization"> + <outNode id="5"/> + </node> + <node id="5" type="End"/> + <node id="6" type="Action" serviceObjectClass="ServiceObject"> + <outNode id="4"/> + </node> + <node id="7" type="Cancel"> + <outNode id="4"/> + </node> +</workflow> Propchange: trunk/Workflow/tests/data/ParallelSplitActionActionCancelCaseSynchronization_1.xml ------------------------------------------------------------------------------ svn:eol-style = native Added: trunk/Workflow/tests/data/ParallelSplitCancelCaseActionActionSynchronization.dot ============================================================================== --- trunk/Workflow/tests/data/ParallelSplitCancelCaseActionActionSynchronization.dot (added) +++ trunk/Workflow/tests/data/ParallelSplitCancelCaseActionActionSynchronization.dot [iso-8859-1] Mon Jan 21 17:04:16 2008 @@ -1,0 +1,18 @@ +digraph ParallelSplitCancelCaseActionActionSynchronization { +node1 [label="Start"] +node2 [label="Parallel Split"] +node3 [label="Cancel"] +node4 [label="Synchronization"] +node5 [label="End"] +node6 [label=""] +node7 [label=""] + +node1 -> node2 +node2 -> node3 +node2 -> node6 +node2 -> node7 +node3 -> node4 +node4 -> node5 +node6 -> node4 +node7 -> node4 +} Propchange: trunk/Workflow/tests/data/ParallelSplitCancelCaseActionActionSynchronization.dot ------------------------------------------------------------------------------ svn:eol-style = native Added: trunk/Workflow/tests/data/ParallelSplitCancelCaseActionActionSynchronization_1.xml ============================================================================== --- trunk/Workflow/tests/data/ParallelSplitCancelCaseActionActionSynchronization_1.xml (added) +++ trunk/Workflow/tests/data/ParallelSplitCancelCaseActionActionSynchronization_1.xml [iso-8859-1] Mon Jan 21 17:04:16 2008 @@ -1,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<workflow name="ParallelSplitCancelCaseActionActionSynchronization" version="1"> + <node id="1" type="Start"> + <outNode id="2"/> + </node> + <node id="2" type="ParallelSplit"> + <outNode id="3"/> + <outNode id="6"/> + <outNode id="7"/> + </node> + <node id="3" type="Cancel"> + <outNode id="4"/> + </node> + <node id="4" type="Synchronization"> + <outNode id="5"/> + </node> + <node id="5" type="End"/> + <node id="6" type="Action" serviceObjectClass="ServiceObject"> + <outNode id="4"/> + </node> + <node id="7" type="Action" serviceObjectClass="ServiceObject"> + <outNode id="4"/> + </node> +</workflow> Propchange: trunk/Workflow/tests/data/ParallelSplitCancelCaseActionActionSynchronization_1.xml ------------------------------------------------------------------------------ svn:eol-style = native Modified: trunk/Workflow/tests/definition_xml_test.php ============================================================================== --- trunk/Workflow/tests/definition_xml_test.php [iso-8859-1] (original) +++ trunk/Workflow/tests/definition_xml_test.php [iso-8859-1] Mon Jan 21 17:04:16 2008 @@ -274,6 +274,28 @@ ); } + public function testSaveParallelSplitCancelCaseActionActionSynchronization() + { + $this->setUpCancelCase( 'first' ); + $this->definition->save( $this->workflow ); + + $this->assertEquals( + $this->readExpected( 'ParallelSplitCancelCaseActionActionSynchronization' ), + $this->readActual( 'ParallelSplitCancelCaseActionActionSynchronization' ) + ); + } + + public function testSaveParallelSplitActionActionCancelCaseSynchronization() + { + $this->setUpCancelCase( 'last' ); + $this->definition->save( $this->workflow ); + + $this->assertEquals( + $this->readExpected( 'ParallelSplitActionActionCancelCaseSynchronization' ), + $this->readActual( 'ParallelSplitActionActionCancelCaseSynchronization' ) + ); + } + public function testLoadStartEnd() { $this->workflow = $this->definition->loadByName( 'StartEnd' ); @@ -512,6 +534,28 @@ $this->assertEquals( $this->readExpected( 'NestedLoops' ), $this->readActual( 'NestedLoops' ) + ); + } + + public function testLoadParallelSplitCancelCaseActionActionSynchronization() + { + $this->workflow = $this->definition->loadByName( 'ParallelSplitCancelCaseActionActionSynchronization' ); + $this->definition->save( $this->workflow ); + + $this->assertEquals( + $this->readExpected( 'ParallelSplitCancelCaseActionActionSynchronization' ), + $this->readActual( 'ParallelSplitCancelCaseActionActionSynchronization' ) + ); + } + + public function testLoadParallelSplitActionActionCancelCaseSynchronization() + { + $this->workflow = $this->definition->loadByName( 'ParallelSplitActionActionCancelCaseSynchronization' ); + $this->definition->save( $this->workflow ); + + $this->assertEquals( + $this->readExpected( 'ParallelSplitActionActionCancelCaseSynchronization' ), + $this->readActual( 'ParallelSplitActionActionCancelCaseSynchronization' ) ); } Modified: trunk/Workflow/tests/execution_test.php ============================================================================== --- trunk/Workflow/tests/execution_test.php [iso-8859-1] (original) +++ trunk/Workflow/tests/execution_test.php [iso-8859-1] Mon Jan 21 17:04:16 2008 @@ -35,6 +35,7 @@ $this->execution->workflow = $this->workflow; $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -46,6 +47,7 @@ $this->execution->workflow = $this->workflow; $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -59,6 +61,7 @@ $this->execution->setInputVariable( 'variable', 'value' ); $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -81,6 +84,7 @@ $this->assertArrayHasKey( 'variable', $e->errors ); $this->assertContains( 'is string', $e->errors ); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertFalse( $this->execution->hasEnded() ); $this->assertTrue( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -178,6 +182,7 @@ $this->execution->workflow = $this->workflow; $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -189,6 +194,7 @@ $this->execution->workflow = $this->workflow; $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -200,6 +206,7 @@ $this->execution->workflow = $this->workflow; $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -211,6 +218,7 @@ $this->execution->workflow = $this->workflow; $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -223,6 +231,7 @@ $this->execution->workflow = $this->workflow; $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -269,6 +278,7 @@ $this->execution->workflow = $this->workflow; $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -281,6 +291,7 @@ $this->execution->workflow = $this->workflow; $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -293,6 +304,7 @@ $this->execution->setVariables( array( 'foo' => 'bar', 'bar' => 'foo' ) ); $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -327,6 +339,7 @@ $this->execution->setVariables( array( 'condition' => true ) ); $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -339,6 +352,7 @@ $this->execution->setVariables( array( 'condition' => false ) ); $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -397,6 +411,7 @@ $this->execution->setVariables( array( 'condition' => true ) ); $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -410,6 +425,7 @@ $this->execution->setVariables( array( 'condition' => false ) ); $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -423,6 +439,7 @@ $this->execution->setVariables( array( 'condition' => false ) ); $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -438,6 +455,7 @@ $this->execution->setVariables( array( 'condition' => true ) ); $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -452,6 +470,7 @@ $this->execution->workflow = $this->workflow; $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -467,6 +486,7 @@ $this->execution->workflow = $this->workflow; $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -482,6 +502,7 @@ $this->execution->workflow = $this->workflow; $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -496,6 +517,7 @@ $this->execution->workflow = $this->workflow; $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -507,6 +529,7 @@ $this->execution->workflow = $this->workflow; $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -519,6 +542,7 @@ $this->execution->workflow = $this->workflow; $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -554,6 +578,7 @@ $this->execution->setInputVariableForSubWorkflow( 'variable', 'value' ); $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -565,6 +590,7 @@ $this->execution->workflow = $this->workflow; $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -598,6 +624,7 @@ $this->execution->workflow = $this->workflow; $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); @@ -613,12 +640,37 @@ $this->execution->workflow = $this->workflow; $this->execution->start(); + $this->assertFalse( $this->execution->isCancelled() ); $this->assertTrue( $this->execution->hasEnded() ); $this->assertFalse( $this->execution->isResumed() ); $this->assertFalse( $this->execution->isSuspended() ); $this->assertEquals( 1, $this->execution->getVariable( 'x' ) ); $this->assertEquals( 2, $this->execution->getVariable( 'z' ) ); + } + + public function testExecuteParallelSplitCancelCaseActionActionSynchronization() + { + $this->setUpCancelCase( 'first' ); + $this->execution->workflow = $this->workflow; + $this->execution->start(); + + $this->assertTrue( $this->execution->isCancelled() ); + $this->assertTrue( $this->execution->hasEnded() ); + $this->assertFalse( $this->execution->isResumed() ); + $this->assertFalse( $this->execution->isSuspended() ); + } + + public function testExecuteParallelSplitActionActionCancelCaseSynchronization() + { + $this->setUpCancelCase( 'last' ); + $this->execution->workflow = $this->workflow; + $this->execution->start(); + + $this->assertTrue( $this->execution->isCancelled() ); + $this->assertTrue( $this->execution->hasEnded() ); + $this->assertFalse( $this->execution->isResumed() ); + $this->assertFalse( $this->execution->isSuspended() ); } public function testListener() Modified: trunk/Workflow/tests/visitor_visualization_test.php ============================================================================== --- trunk/Workflow/tests/visitor_visualization_test.php [iso-8859-1] (original) +++ trunk/Workflow/tests/visitor_visualization_test.php [iso-8859-1] Mon Jan 21 17:04:16 2008 @@ -227,6 +227,28 @@ ); } + public function testVisitParallelSplitCancelCaseActionActionSynchronization() + { + $this->setUpCancelCase( 'first' ); + $this->workflow->accept( $this->visitor ); + + $this->assertEquals( + $this->readExpected( 'ParallelSplitCancelCaseActionActionSynchronization' ), + $this->visitor->__toString() + ); + } + + public function testVisitParallelSplitActionActionCancelCaseSynchronization() + { + $this->setUpCancelCase( 'last' ); + $this->workflow->accept( $this->visitor ); + + $this->assertEquals( + $this->readExpected( 'ParallelSplitActionActionCancelCaseSynchronization' ), + $this->visitor->__toString() + ); + } + protected function readExpected( $name ) { return file_get_contents( Modified: trunk/WorkflowDatabaseTiein/ChangeLog ============================================================================== --- trunk/WorkflowDatabaseTiein/ChangeLog [iso-8859-1] (original) +++ trunk/WorkflowDatabaseTiein/ChangeLog [iso-8859-1] Mon Jan 21 17:04:16 2008 @@ -1,3 +1,5 @@ +- Implemented issue #10941: Support for the Cancel Case workflow pattern. + - Fixed issue #12403: Multiple construction of ezcWorkflowDatabaseDefinitionStorage. Modified: trunk/WorkflowDatabaseTiein/src/execution.php ============================================================================== --- trunk/WorkflowDatabaseTiein/src/execution.php [iso-8859-1] (original) +++ trunk/WorkflowDatabaseTiein/src/execution.php [iso-8859-1] Mon Jan 21 17:04:16 2008 @@ -136,7 +136,10 @@ $this->cleanupTable( 'execution' ); $this->cleanupTable( 'execution_state' ); - $this->db->commit(); + if ( !$this->isCancelled() ) + { + $this->db->commit(); + } } /** Added: trunk/WorkflowEventLogTiein/tests/data/ParallelSplitActionActionCancelCaseSynchronization.log ============================================================================== --- trunk/WorkflowEventLogTiein/tests/data/ParallelSplitActionActionCancelCaseSynchronization.log (added) +++ trunk/WorkflowEventLogTiein/tests/data/ParallelSplitActionActionCancelCaseSynchronization.log [iso-8859-1] Mon Jan 21 17:04:16 2008 @@ -1,0 +1,19 @@ +MMM DD HH:MM:SS [Info] [default] [default] Started execution #1 of workflow "ParallelSplitActionActionCancelCaseSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Activated node #1(ezcWorkflowNodeStart) for instance #1 of workflow "ParallelSplitActionActionCancelCaseSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Started thread #0 (1 sibling(s)) for execution #1 of workflow "ParallelSplitActionActionCancelCaseSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Activated node #2(ezcWorkflowNodeParallelSplit) for instance #1 of workflow "ParallelSplitActionActionCancelCaseSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Executed node #1(ezcWorkflowNodeStart) for instance #1 of workflow "ParallelSplitActionActionCancelCaseSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Started thread #1 (3 sibling(s)) for execution #1 of workflow "ParallelSplitActionActionCancelCaseSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Activated node #3(ezcWorkflowNodeAction) for instance #1 of workflow "ParallelSplitActionActionCancelCaseSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Started thread #2 (3 sibling(s)) for execution #1 of workflow "ParallelSplitActionActionCancelCaseSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Activated node #6(ezcWorkflowNodeAction) for instance #1 of workflow "ParallelSplitActionActionCancelCaseSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Started thread #3 (3 sibling(s)) for execution #1 of workflow "ParallelSplitActionActionCancelCaseSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Activated node #7(ezcWorkflowNodeCancel) for instance #1 of workflow "ParallelSplitActionActionCancelCaseSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Executed node #2(ezcWorkflowNodeParallelSplit) for instance #1 of workflow "ParallelSplitActionActionCancelCaseSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Activated node #4(ezcWorkflowNodeSynchronization) for instance #1 of workflow "ParallelSplitActionActionCancelCaseSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Executed node #3(ezcWorkflowNodeAction) for instance #1 of workflow "ParallelSplitActionActionCancelCaseSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Executed node #6(ezcWorkflowNodeAction) for instance #1 of workflow "ParallelSplitActionActionCancelCaseSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Could not roll back service object "ServiceObject" of node #3 for instance #1 of workflow "ParallelSplitActionActionCancelCaseSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Could not roll back service object "ServiceObject" of node #6 for instance #1 of workflow "ParallelSplitActionActionCancelCaseSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Executed node #7(ezcWorkflowNodeCancel) for instance #1 of workflow "ParallelSplitActionActionCancelCaseSynchronization" (version 1). +MMM DD HH:MM:SS [Info] [default] [default] Cancelled execution #1 of workflow "ParallelSplitActionActionCancelCaseSynchronization" (version 1). Propchange: trunk/WorkflowEventLogTiein/tests/data/ParallelSplitActionActionCancelCaseSynchronization.log ------------------------------------------------------------------------------ svn:eol-style = native Added: trunk/WorkflowEventLogTiein/tests/data/ParallelSplitCancelCaseActionActionSynchronization.log ============================================================================== --- trunk/WorkflowEventLogTiein/tests/data/ParallelSplitCancelCaseActionActionSynchronization.log (added) +++ trunk/WorkflowEventLogTiein/tests/data/ParallelSplitCancelCaseActionActionSynchronization.log [iso-8859-1] Mon Jan 21 17:04:16 2008 @@ -1,0 +1,14 @@ +MMM DD HH:MM:SS [Info] [default] [default] Started execution #1 of workflow "ParallelSplitCancelCaseActionActionSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Activated node #1(ezcWorkflowNodeStart) for instance #1 of workflow "ParallelSplitCancelCaseActionActionSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Started thread #0 (1 sibling(s)) for execution #1 of workflow "ParallelSplitCancelCaseActionActionSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Activated node #2(ezcWorkflowNodeParallelSplit) for instance #1 of workflow "ParallelSplitCancelCaseActionActionSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Executed node #1(ezcWorkflowNodeStart) for instance #1 of workflow "ParallelSplitCancelCaseActionActionSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Started thread #1 (3 sibling(s)) for execution #1 of workflow "ParallelSplitCancelCaseActionActionSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Activated node #3(ezcWorkflowNodeCancel) for instance #1 of workflow "ParallelSplitCancelCaseActionActionSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Started thread #2 (3 sibling(s)) for execution #1 of workflow "ParallelSplitCancelCaseActionActionSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Activated node #6(ezcWorkflowNodeAction) for instance #1 of workflow "ParallelSplitCancelCaseActionActionSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Started thread #3 (3 sibling(s)) for execution #1 of workflow "ParallelSplitCancelCaseActionActionSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Activated node #7(ezcWorkflowNodeAction) for instance #1 of workflow "ParallelSplitCancelCaseActionActionSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Executed node #2(ezcWorkflowNodeParallelSplit) for instance #1 of workflow "ParallelSplitCancelCaseActionActionSynchronization" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Executed node #3(ezcWorkflowNodeCancel) for instance #1 of workflow "ParallelSplitCancelCaseActionActionSynchronization" (version 1). +MMM DD HH:MM:SS [Info] [default] [default] Cancelled execution #1 of workflow "ParallelSplitCancelCaseActionActionSynchronization" (version 1). Propchange: trunk/WorkflowEventLogTiein/tests/data/ParallelSplitCancelCaseActionActionSynchronization.log ------------------------------------------------------------------------------ svn:eol-style = native Added: trunk/WorkflowEventLogTiein/tests/data/ParallelSplitSynchronizationCancelCase.log ============================================================================== --- trunk/WorkflowEventLogTiein/tests/data/ParallelSplitSynchronizationCancelCase.log (added) +++ trunk/WorkflowEventLogTiein/tests/data/ParallelSplitSynchronizationCancelCase.log [iso-8859-1] Mon Jan 21 17:04:16 2008 @@ -1,0 +1,14 @@ +MMM DD HH:MM:SS [Info] [default] [default] Started execution #1 of workflow "ParallelSplitSynchronizationCancelCase" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Activated node #1(ezcWorkflowNodeStart) for instance #1 of workflow "ParallelSplitSynchronizationCancelCase" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Started thread #0 (1 sibling(s)) for execution #1 of workflow "ParallelSplitSynchronizationCancelCase" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Activated node #2(ezcWorkflowNodeParallelSplit) for instance #1 of workflow "ParallelSplitSynchronizationCancelCase" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Executed node #1(ezcWorkflowNodeStart) for instance #1 of workflow "ParallelSplitSynchronizationCancelCase" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Started thread #1 (3 sibling(s)) for execution #1 of workflow "ParallelSplitSynchronizationCancelCase" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Activated node #3(ezcWorkflowNodeCancel) for instance #1 of workflow "ParallelSplitSynchronizationCancelCase" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Started thread #2 (3 sibling(s)) for execution #1 of workflow "ParallelSplitSynchronizationCancelCase" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Activated node #6(ezcWorkflowNodeAction) for instance #1 of workflow "ParallelSplitSynchronizationCancelCase" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Started thread #3 (3 sibling(s)) for execution #1 of workflow "ParallelSplitSynchronizationCancelCase" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Activated node #7(ezcWorkflowNodeAction) for instance #1 of workflow "ParallelSplitSynchronizationCancelCase" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Executed node #2(ezcWorkflowNodeParallelSplit) for instance #1 of workflow "ParallelSplitSynchronizationCancelCase" (version 1). +MMM DD HH:MM:SS [Debug] [default] [default] Executed node #3(ezcWorkflowNodeCancel) for instance #1 of workflow "ParallelSplitSynchronizationCancelCase" (version 1). +MMM DD HH:MM:SS [Info] [default] [default] Cancelled execution #1 of workflow "ParallelSplitSynchronizationCancelCase" (version 1). Propchange: trunk/WorkflowEventLogTiein/tests/data/ParallelSplitSynchronizationCancelCase.log ------------------------------------------------------------------------------ svn:eol-style = native Modified: trunk/WorkflowEventLogTiein/tests/listener_test.php ============================================================================== --- trunk/WorkflowEventLogTiein/tests/listener_test.php [iso-8859-1] (original) +++ trunk/WorkflowEventLogTiein/tests/listener_test.php [iso-8859-1] Mon Jan 21 17:04:16 2008 @@ -344,5 +344,31 @@ $this->readActual() ); } + + public function testLogParallelSplitCancelCaseActionActionSynchronization() + { + $this->setUpCancelCase( 'first' ); + $this->definition->save( $this->workflow ); + $this->execution->workflow = $this->workflow; + $this->execution->start(); + + $this->assertEquals( + $this->readExpected( 'ParallelSplitCancelCaseActionActionSynchronization' ), + $this->readActual() + ); + } + + public function testLogParallelSplitActionActionCancelCaseSynchronization() + { + $this->setUpCancelCase( 'last' ); + $this->definition->save( $this->workflow ); + $this->execution->workflow = $this->workflow; + $this->execution->start(); + + $this->assertEquals( + $this->readExpected( 'ParallelSplitActionActionCancelCaseSynchronization' ), + $this->readActual() + ); + } } ?> -- svn-components mailing list svn-components@lists.ez.no http://lists.ez.no/mailman/listinfo/svn-components