%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /home/bitrix/www/bitrix/modules/bizproc/classes/general/
Upload File :
Create Path :
Current File : /home/bitrix/www/bitrix/modules/bizproc/classes/general/workflow.php

<?
IncludeModuleLangFile(__FILE__);

/**
* Workflow instance.
*/
class CBPWorkflow
{
	private $isNew = false;
	private $instanceId = "";

	/** @var CBPRuntime|null $runtime */
	private $runtime = null;

	private $rootActivity = null;

	private $activitiesQueue = array();
	private $eventsQueue = array();

	private $activitiesNamesMap = array();

	/************************  PROPERTIES  *******************************/

	public function GetInstanceId()
	{
		return $this->instanceId;
	}

	/**
	 * @return CBPRuntime
	 */
	public function GetRuntime()
	{
		return $this->runtime;
	}

	private function GetWorkflowStatus()
	{
		return $this->rootActivity->GetWorkflowStatus();
	}

	private function SetWorkflowStatus($newStatus)
	{
		$this->rootActivity->SetWorkflowStatus($newStatus);
		$this->GetRuntime()->onWorkflowStatusChanged($this->GetInstanceId(), $newStatus);
	}

	public function GetService($name)
	{
		return $this->runtime->GetService($name);
	}

	public function GetDocumentId()
	{
		return $this->rootActivity->GetDocumentId();
	}

	/************************  CONSTRUCTORS  ****************************************************/

	/**
	* Public constructor initializes a new workflow instance with the specified ID.
	* 
	* @param mixed $instanceId - ID of the new workflow instance.
	* @param mixed $runtime - Runtime object.
	* @return CBPWorkflow
	*/
	public function __construct($instanceId, CBPRuntime $runtime)
	{
		if (strlen($instanceId) <= 0)
			throw new Exception("instanceId");
		if (!$runtime)
			throw new Exception("runtime");

		$this->instanceId = $instanceId;
		$this->runtime = $runtime;
	}

	/**
	 * Remove workflow object from serialized data
	 * @return array
	 */
	public function __sleep()
	{
		return array();
	}

	/************************  CREATE / LOAD WORKFLOW  ****************************************/

	public function Initialize(CBPActivity $rootActivity, $documentId, $workflowParameters = array(), $workflowVariablesTypes = array(), $workflowParametersTypes = array(), $workflowTemplateId = 0)
	{
		$this->rootActivity = $rootActivity;
		$rootActivity->SetWorkflow($this);
		if (method_exists($rootActivity, 'SetWorkflowTemplateId'))
			$rootActivity->SetWorkflowTemplateId($workflowTemplateId);

		$arDocumentId = CBPHelper::ParseDocumentId($documentId);

		$rootActivity->SetDocumentId($arDocumentId);

		$documentService = $this->GetService("DocumentService");
		$documentType = isset($workflowParameters[CBPDocument::PARAM_DOCUMENT_TYPE]) ?
			$workflowParameters[CBPDocument::PARAM_DOCUMENT_TYPE]
			: $documentService->GetDocumentType($arDocumentId);

		unset($workflowParameters[CBPDocument::PARAM_DOCUMENT_TYPE]);

		if ($documentType !== null)
		{
			$rootActivity->SetDocumentType($documentType);
			$rootActivity->SetFieldTypes($documentService->GetDocumentFieldTypes($documentType));
		}

		$rootActivity->SetProperties($workflowParameters);


		$rootActivity->SetVariablesTypes($workflowVariablesTypes);
		if (is_array($workflowVariablesTypes))
		{
			foreach ($workflowVariablesTypes as $k => $v)
				$rootActivity->SetVariable($k, $v["Default"]);
		}

		$rootActivity->SetPropertiesTypes($workflowParametersTypes);
	}

	public function Reload(CBPActivity $rootActivity)
	{
		$this->rootActivity = $rootActivity;
		$rootActivity->SetWorkflow($this);

		switch ($this->GetWorkflowStatus())
		{
			case CBPWorkflowStatus::Completed:
			case CBPWorkflowStatus::Terminated:
				throw new Exception("InvalidAttemptToLoad");
		}
	}

	public function OnRuntimeStopped()
	{
		$workflowStatus = $this->GetWorkflowStatus();

		if ($workflowStatus == CBPWorkflowStatus::Suspended)
			return;

		if ($this->rootActivity->executionStatus == CBPActivityExecutionStatus::Closed)
		{
			$this->SetWorkflowStatus(CBPWorkflowStatus::Completed);
		}
		else
		{
			$workflowStatus = $this->GetWorkflowStatus();
			if ($workflowStatus == CBPWorkflowStatus::Running)
				$this->SetWorkflowStatus(CBPWorkflowStatus::Suspended);
		}

		$persister = CBPWorkflowPersister::GetPersister();
		$persister->SaveWorkflow($this->rootActivity, true);
	}

	/************************  EXECUTE WORKFLOW  ************************************************/

	/**
	* Starts new workflow instance.
	* 
	*/
	public function Start()
	{
		if ($this->GetWorkflowStatus() != CBPWorkflowStatus::Created)
			throw new Exception("CanNotStartInstanceTwice");

		$this->isNew = true;
		$this->SetWorkflowStatus(CBPWorkflowStatus::Running);

		try
		{
			$this->InitializeActivity($this->rootActivity);
			$this->ExecuteActivity($this->rootActivity);
			$this->RunQueue();
		}
		catch (Exception $e)
		{
			$this->Terminate($e);
			throw $e;
		}

		if ($this->rootActivity->executionStatus == CBPActivityExecutionStatus::Closed)
		{
			$this->SetWorkflowStatus(CBPWorkflowStatus::Completed);
		}
		else
		{
			$workflowStatus = $this->GetWorkflowStatus();
			if ($workflowStatus == CBPWorkflowStatus::Running)
				$this->SetWorkflowStatus(CBPWorkflowStatus::Suspended);
		}

		$persister = CBPWorkflowPersister::GetPersister();
		$persister->SaveWorkflow($this->rootActivity, true);
	}

	/**
	* Resume existing workflow.
	* 
	*/
	public function Resume()
	{
		if ($this->GetWorkflowStatus() != CBPWorkflowStatus::Suspended)
			throw new Exception("CanNotResumeInstance");

		$this->SetWorkflowStatus(CBPWorkflowStatus::Running);

		try
		{
			$this->RunQueue();
		}
		catch (Exception $e)
		{
			$this->Terminate($e);
			throw $e;
		}

		if ($this->rootActivity->executionStatus == CBPActivityExecutionStatus::Closed)
		{
			$this->SetWorkflowStatus(CBPWorkflowStatus::Completed);
		}
		else
		{
			$workflowStatus = $this->GetWorkflowStatus();
			if ($workflowStatus == CBPWorkflowStatus::Running)
				$this->SetWorkflowStatus(CBPWorkflowStatus::Suspended);
		}

		$persister = CBPWorkflowPersister::GetPersister();
		$persister->SaveWorkflow($this->rootActivity, true);
	}

	public function isNew()
	{
		return $this->isNew;
	}

	/**********************  EXTERNAL EVENTS  **************************************************************/

	/**
	* Resume the workflow instance and transfer the specified event to it.
	* 
	* @param mixed $eventName - Event name.
	* @param mixed $arEventParameters - Event parameters.
	*/
	public function SendExternalEvent($eventName, $arEventParameters = array())
	{
		$this->AddEventToQueue($eventName, $arEventParameters);
		$this->Resume();
	}

	/***********************  SEARCH ACTIVITY BY NAME  ****************************************************/

	private function FillNameActivityMapInternal(CBPActivity $activity)
	{
		$this->activitiesNamesMap[$activity->GetName()] = $activity;

		if (is_a($activity, "CBPCompositeActivity"))
		{
			$arSubActivities = $activity->CollectNestedActivities();
			foreach ($arSubActivities as $subActivity)
				$this->FillNameActivityMapInternal($subActivity);
		}
	}

	private function FillNameActivityMap()
	{
		if (!is_array($this->activitiesNamesMap))
			$this->activitiesNamesMap = array();

		if (count($this->activitiesNamesMap) > 0)
			return;

		$this->FillNameActivityMapInternal($this->rootActivity);
	}

	/**
	* Returns activity by its name.
	* 
	* @param mixed $activityName - Activity name.
	* @return CBPActivity - Returns activity object or null if activity is not found.
	*/
	public function GetActivityByName($activityName)
	{
		if (strlen($activityName) <= 0)
			throw new Exception("activityName");

		$activity = null;

		$this->FillNameActivityMap();

		if (array_key_exists($activityName, $this->activitiesNamesMap))
			$activity = $this->activitiesNamesMap[$activityName];

		return $activity;
	}

	/************************  ACTIVITY EXECUTION  *************************************************/

	/**
	* Initializes the specified activity by calling its method Initialize.
	* 
	* @param CBPActivity $activity
	*/
	public function InitializeActivity(CBPActivity $activity)
	{
		if ($activity == null)
			throw new CBPArgumentNullException("activity");

		if ($activity->executionStatus != CBPActivityExecutionStatus::Initialized)
			throw new Exception("InvalidInitializingState");

		$activity->Initialize();
	}

	/**
	* Plans specified activity for execution.
	* 
	* @param CBPActivity $activity - Activity object.
	* @param mixed $arEventParameters - Optional parameters.
	*/
	public function ExecuteActivity(CBPActivity $activity, $arEventParameters = array())
	{
		if ($activity == null)
			throw new Exception("activity");

		if ($activity->executionStatus != CBPActivityExecutionStatus::Initialized)
			throw new Exception("InvalidExecutionState");

		$activity->SetStatus(CBPActivityExecutionStatus::Executing, $arEventParameters);
		$this->AddItemToQueue(array($activity, CBPActivityExecutorOperationType::Execute));
	}

	/**
	* Close specified activity.
	* 
	* @param CBPActivity $activity - Activity object.
	* @param mixed $arEventParameters - Optional parameters.
	*/
	public function CloseActivity(CBPActivity $activity, $arEventParameters = array())
	{
		switch ($activity->executionStatus)
		{
			case CBPActivityExecutionStatus::Executing:
				$activity->MarkCompleted($arEventParameters);
				return;

			case CBPActivityExecutionStatus::Canceling:
				$activity->MarkCanceled($arEventParameters);
				return;

			case CBPActivityExecutionStatus::Closed:
				return;

			case CBPActivityExecutionStatus::Faulting:
				$activity->MarkFaulted($arEventParameters);
				return;
		}

		throw new Exception("InvalidClosingState");
	}

	/**
	* Cancel specified activity.
	* 
	* @param CBPActivity $activity - Activity object.
	* @param mixed $arEventParameters - Optional parameters.
	*/
	public function CancelActivity(CBPActivity $activity, $arEventParameters = array())
	{
		if ($activity == null)
			throw new Exception("activity");

		if ($activity->executionStatus != CBPActivityExecutionStatus::Executing)
			throw new Exception("InvalidCancelingState");

		$activity->SetStatus(CBPActivityExecutionStatus::Canceling, $arEventParameters);
		$this->AddItemToQueue(array($activity, CBPActivityExecutorOperationType::Cancel));
	}

	public function FaultActivity(CBPActivity $activity, Exception $e, $arEventParameters = array())
	{
		if ($activity == null)
			throw new Exception("activity");

		if ($activity->executionStatus == CBPActivityExecutionStatus::Closed)
		{
			if ($activity->parent == null)
				$this->Terminate($e);
			else
				$this->FaultActivity($activity->parent, $e, $arEventParameters);
		}
		else
		{
			$activity->SetStatus(CBPActivityExecutionStatus::Faulting);
			$this->AddItemToQueue(array($activity, CBPActivityExecutorOperationType::HandleFault, $e));
		}
	}

	/************************  ACTIVITIES QUEUE  ***********************************************/

	private function AddItemToQueue($item)
	{
		array_push($this->activitiesQueue, $item);
	}

	private function RunQueue()
	{
		while (true)
		{
			$this->ProcessQueuedEvents();

			$item = array_shift($this->activitiesQueue);
			if ($item == null)
				return;

			try
			{
				$this->RunQueuedItem($item[0], $item[1], (count($item) > 2 ? $item[2] : null));
			}
			catch (Exception $e)
			{
				$this->FaultActivity($item[0], $e);

				if ($this->GetWorkflowStatus() == CBPWorkflowStatus::Terminated)
					return;
			}
		}
	}

	private function RunQueuedItem(CBPActivity $activity, $activityOperation, Exception $exception = null)
	{
		/** @var $trackingService CBPTrackingService */
		if ($activityOperation == CBPActivityExecutorOperationType::Execute)
		{
			if ($activity->executionStatus == CBPActivityExecutionStatus::Executing)
			{
				try
				{
					$trackingService = $this->GetService("TrackingService");
					$trackingService->Write($this->GetInstanceId(), CBPTrackingType::ExecuteActivity, $activity->GetName(), $activity->executionStatus, $activity->executionResult, ($activity->IsPropertyExists("Title") ? $activity->Title : ""), "");
					$newStatus = $activity->Execute();

					if ($newStatus == CBPActivityExecutionStatus::Closed)
						$this->CloseActivity($activity);
					elseif ($newStatus != CBPActivityExecutionStatus::Executing)
						throw new Exception("InvalidExecutionStatus");
				}
				catch (Exception $e)
				{
					throw $e;
				}
			}
		}
		elseif ($activityOperation == CBPActivityExecutorOperationType::Cancel)
		{
			if ($activity->executionStatus == CBPActivityExecutionStatus::Canceling)
			{
				try
				{
					$trackingService = $this->GetService("TrackingService");
					$trackingService->Write($this->GetInstanceId(), CBPTrackingType::CancelActivity, $activity->GetName(), $activity->executionStatus, $activity->executionResult, ($activity->IsPropertyExists("Title") ? $activity->Title : ""), "");

					$newStatus = $activity->Cancel();

					if ($newStatus == CBPActivityExecutionStatus::Closed)
						$this->CloseActivity($activity);
					elseif ($newStatus != CBPActivityExecutionStatus::Canceling)
						throw new Exception("InvalidExecutionStatus");
				}
				catch (Exception $e)
				{
					throw $e;
				}
			}
		}
		elseif ($activityOperation == CBPActivityExecutorOperationType::HandleFault)
		{
			if ($activity->executionStatus == CBPActivityExecutionStatus::Faulting)
			{
				try
				{
					$trackingService = $this->GetService("TrackingService");
					$trackingService->Write($this->GetInstanceId(), CBPTrackingType::FaultActivity, $activity->GetName(), $activity->executionStatus, $activity->executionResult, ($activity->IsPropertyExists("Title") ? $activity->Title : ""), ($exception != null ? ($exception->getCode()? "[".$exception->getCode()."] " : '').$exception->getMessage() : ""));

					$newStatus = $activity->HandleFault($exception);

					if ($newStatus == CBPActivityExecutionStatus::Closed)
						$this->CloseActivity($activity);
					elseif ($newStatus != CBPActivityExecutionStatus::Faulting)
						throw new Exception("InvalidExecutionStatus");
				}
				catch (Exception $e)
				{
					throw $e;
				}
			}
		}
	}

	public function Terminate(Exception $e = null, $stateTitle = '')
	{
		/** @var CBPTaskService $taskService */
		$taskService = $this->GetService("TaskService");
		$taskService->DeleteAllWorkflowTasks($this->GetInstanceId());

		$this->SetWorkflowStatus(CBPWorkflowStatus::Terminated);

		$persister = CBPWorkflowPersister::GetPersister();
		$persister->SaveWorkflow($this->rootActivity, true);

		/** @var CBPStateService $stateService */
		$stateService = $this->GetService("StateService");
		$stateService->SetState(
			$this->instanceId,
			array(
				"STATE" => "Terminated",
				"TITLE" => $stateTitle ? $stateTitle : GetMessage("BPCGWF_TERMINATED"),
				"PARAMETERS" => array()
			),
			false//array()
		);

		if ($e != null)
		{
			$trackingService = $this->GetService("TrackingService");
			$trackingService->Write($this->instanceId, CBPTrackingType::FaultActivity, "none", CBPActivityExecutionStatus::Faulting, CBPActivityExecutionResult::Faulted, "Exception", ($e->getCode()? "[".$e->getCode()."] " : '').$e->getMessage());
		}
	}

	/**
	 * @param CBPActivity $activity
	 * @throws CBPArgumentNullException
	 * @throws Exception
	 */
	public function FinalizeActivity(CBPActivity $activity)
	{
		if ($activity == null)
			throw new CBPArgumentNullException("activity");

		//if ($activity->executionStatus != CBPActivityExecutionStatus::Closed)
		//	throw new Exception("InvalidFinalizingState");

		$activity->Finalize();
	}

	/************************  EVENTS QUEUE  ********************************************************/

	private function AddEventToQueue($eventName, $arEventParameters = array())
	{
		array_push($this->eventsQueue, array($eventName, $arEventParameters));
	}

	private function ProcessQueuedEvents()
	{
		while (true)
		{
			$arEvent = array_shift($this->eventsQueue);
			if ($arEvent == null)
				return;

			$eventName = $arEvent[0];
			$arEventParameters = $arEvent[1];

			$this->ProcessQueuedEvent($eventName, $arEventParameters);
		}
	}

	private function ProcessQueuedEvent($eventName, $arEventParameters = array())
	{
		if (!array_key_exists($eventName, $this->rootActivity->arEventsMap))
			return;

		foreach ($this->rootActivity->arEventsMap[$eventName] as $eventHandler)
		{
			if (is_a($eventHandler, "IBPActivityExternalEventListener"))
				$eventHandler->OnExternalEvent($arEventParameters);
		}
	}

	/**
	* Add new event handler to the specified event.
	* 
	* @param mixed $eventName - Event name.
	* @param IBPActivityExternalEventListener $eventHandler - Event handler.
	*/
	public function AddEventHandler($eventName, IBPActivityExternalEventListener $eventHandler)
	{
		if (!is_array($this->rootActivity->arEventsMap))
			$this->rootActivity->arEventsMap = array();

		if (!array_key_exists($eventName, $this->rootActivity->arEventsMap))
			$this->rootActivity->arEventsMap[$eventName] = array();

		$this->rootActivity->arEventsMap[$eventName][] = $eventHandler;
	}

	/**
	* Remove the event handler from the specified event.
	* 
	* @param mixed $eventName - Event name.
	* @param IBPActivityExternalEventListener $eventHandler - Event handler.
	*/
	public function RemoveEventHandler($eventName, IBPActivityExternalEventListener $eventHandler)
	{
		if (!is_array($this->rootActivity->arEventsMap))
			$this->rootActivity->arEventsMap = array();

		if (!array_key_exists($eventName, $this->rootActivity->arEventsMap))
			$this->rootActivity->arEventsMap[$eventName] = array();

		$idx = array_search($eventHandler, $this->rootActivity->arEventsMap[$eventName], true);
		if ($idx !== false)
			unset($this->rootActivity->arEventsMap[$eventName][$idx]);

		if (count($this->rootActivity->arEventsMap[$eventName]) <= 0)
			unset($this->rootActivity->arEventsMap[$eventName]);
	}

	/*******************  UTILITIES  ***************************************************************/

	/**
	* Returns available events for current state of state machine workflow activity.
	* 
	*/
	public function GetAvailableStateEvents()
	{
		if (!is_a($this->rootActivity, "CBPStateMachineWorkflowActivity"))
			throw new Exception("NotAStateMachineWorkflow");

		return $this->rootActivity->GetAvailableStateEvents();
	}

}

Zerion Mini Shell 1.0