When you read through the blog post Another 10 Common Mistakes made by Workflow Beginners from Paul Bakker, you can find in section #16 the advice to avoid duplicate workflow instances and the advice
You could set a 'check function' on your event linkage, so that no new workflow is started if an active workflow already exists for the same object. Or, when a change occurs, raise an event to terminate the existing workflow and start a new one.
In this blog I will show you, how to achieve a very safe singleton concept, starting with
If you would like to check, if there's already the same workflow (i.e. to be exact: "A workflow instance of this object running, that is based on the same workflow pattern as this one"), you'll need two information
- The object instance identification: type and instance key
- The workflow pattern ID
Both is already available in the standard event coupling, if you're not using any specialty here:
to the check function module.
This way, it's possibly to create a generic check function module Z_CHECK_OTHER_WORKFLOW_RUNS, that is working with all workflow implementations instantly:
FUNCTION Z_CHECK_OTHER_WORKFLOW_RUNS.
*"----------------------------------------------------------------------
*"*"Local Interface:
*" IMPORTING
*" VALUE(SENDER) TYPE SIBFLPORB
*" VALUE(EVENT) TYPE SIBFEVENT
*" VALUE(RECTYPE) TYPE SWFERECTYP
*" VALUE(EVENT_CONTAINER) TYPE REF TO IF_SWF_IFS_PARAMETER_CONTAINER
*" EXCEPTIONS
*" OTHER_WORKFLOW_RUNS
*"----------------------------------------------------------------------
"The method generically finds any workflow based on
"the sender object.
DATA: lt_task_filter TYPE STANDARD TABLE
OF SWR_TASK
INITIAL SIZE 2,
lt_worklist TYPE STANDARD TABLE
OF SWR_WIHDR.
DATA: ls_task TYPE swr_task.
ls_task = recType.
APPEND ls_task TO lt_task_filter.
CALL FUNCTION 'SAP_WAPI_WORKITEMS_TO_OBJECT'
EXPORTING
OBJECT_POR = sender
TOP_LEVEL_ITEMS = 'X'
TEXT = ' '
OUTPUT_ONLY_TOP_LEVEL = 'X'
TABLES
TASK_FILTER = lt_task_filter
WORKLIST = lt_worklist.
"Check running. If error-workflows, then start another instance
READ TABLE lt_worklist TRANSPORTING NO FIELDS
WITH KEY wi_stat = 'STARTED'.
IF sy-subrc = 0.
RAISE OTHER_WORKFLOW_RUNS. "Use better MESSAGE E... RAISING ...
ENDIF.
"You may also check for wi_stat = 'ERROR' if you like that
ENDFUNCTION.
This looks already like we're done, and for normal cases it's working fine. However, the story continues, as soon as you have multiple starting events at the same time (right after posting a document or later on through a change document). In such cases, if the workflow instances are created simultaneously, each call to SAP_WAPI_WORKITEMS_TO_OBJECT will return that no instance is currently running, and at the end you still have two (or more) workflows running. Just think of the BUS2012.ReleaseStepCreated event, which can occur more than once at the same time.
This leads us to the next section, closing the door via the second workflow design pattern as mentioned by Paul before.
As outlined in chapter 1, it's still possible to have a another workflow instance running, although you're checking that in the check-FM during the creation event, or you just can't check at the time of the event creation and you have to rely on the workflow instance.
This trigger/wait event concept foresees, that each new workflow instance will close all already running workflows in place. So, yes, a new workflow will start, but in ends any other existing way, leaving only one left for the object instance.
As you need the instance identification anyways, I suggest to create an event .CloseProcess on the instance level of the object type, regardless if you're using the BOR design or the new ABAP OO Workflow design.
Within the workflow pattern, you design a sender fork/wait combination like this:
The sending step will raise the event for <instance>.CloseProcess and any other workflow who's already listening to that event (because it's running), will end up immediatly. After that is done, the workflow will itself wait for such an event, just for the case, there's a newer workflow coming up.
The fork will cover the complete workflow implementation. So after receiving the event .CloseProcess for the <instance> you'd immediately need to complete your workflow gracefully.
So, that's all not real magic and you have another advantage, that if you'd like to start all-over with a couple of workflow instance, you can now easily raise the event .CloseProcess for the instance you don't like anymore and create a new start event (if you're including the solution of chapter 1 a new workflow wouldn't start with a second start event, because that is prevented).
However, there's a small piece missing, as I have experienced, that the event processing through the workflow runtime system can take some time, and under certain circumstances it may happen, that the workflow is receiving its own .CloseProcess event. This happens, when the rRFC queue is full and the event creation is delayed. In such cases, the wait step is already created, before the event is receiving, ending up with no workflow running at all.
To solve this, please read the next chapter.
The event trigger for .CloseProcess must provide the own source top-Workflow ID, so that the receiving wait step can identify itself.
Add an event parameter EV_WORKFLOW_ID to the definition for .CloseProcess like this:
Create a dataflow upon creating the event like this now:
Now the trick: Edit the instance linkage for the .CloseProcess event and add a check-function module there (transaction SWEINST):
And when done with it, you'd still need the coding excerpt from the check-FM, so here it comes:
FUNCTION Z_CHECK_NOT_OWN_TOPFLOW.
*"----------------------------------------------------------------------
*"*"Local Interface:
*" IMPORTING
*" VALUE(SENDER) TYPE SIBFLPORB
*" VALUE(EVENT) TYPE SIBFEVENT
*" VALUE(RECTYPE) TYPE SWFERECTYP
*" VALUE(EVENT_CONTAINER) TYPE REF TO IF_SWF_IFS_PARAMETER_CONTAINER
*" EXCEPTIONS
*" THIS_IS_THE_SAME_WORKFLOW
*"----------------------------------------------------------------------
DATA: lv_sending_topflow_ID TYPE swwwihead-wi_id.
TRY.
CALL METHOD event_container->get
EXPORTING
name = 'EV_WORKFLOW_ID'
IMPORTING
value = lv_sending_topflow_id.
IF NOT lv_sending_topflow_id IS INITIAL.
"The top workflow IS present in the event container, so we will check it!
* ** Read workitem instance and container from event
DATA: lv_target_ei_id TYPE swwwihead-wi_id. "Event wait step of target workflow
CALL METHOD event_container->get
EXPORTING
name = evt_receiver_id
IMPORTING
value = lv_target_ei_id.
IF NOT lv_target_ei_id IS INITIAL.
"Yes, so we have not source TopFlow and target EventID.
DATA: lo_wi_handle TYPE REF TO if_swf_run_wim_internal.
DATA: ls_context TYPE sww_wimctx.
DATA: lo_wi_container TYPE REF TO if_swf_cnt_container.
* - set context
ls_context-do_commit = ' '.
ls_context-called_btc = 'X'.
ls_context-exec_user = sy-uname.
ls_context-fbname = 'Z_CHECK_NOT_OWN_TOPFLOW'.
CLEAR lo_wi_handle.
CALL METHOD cl_swf_run_wim_factory=>initialize( ).
CALL METHOD cl_swf_run_wim_factory=>find_by_wiid
EXPORTING
im_wiid = lv_target_ei_id
im_read_for_update = ' '
im_context = ls_context
RECEIVING
re_instance = lo_wi_handle.
IF NOT lo_wi_handle IS INITIAL.
DATA: ls_swwwihead_of_target_EI TYPE swwwihead.
ls_swwwihead_of_target_EI = lo_wi_handle->get_wi_header( ).
* *********************************************************************
*
* COMPARE the sending top-workflow against the receiving top-workflow
*
* *********************************************************************
IF ls_swwwihead_of_target_EI-top_wi_id = lv_sending_topflow_ID.
MESSAGE E005 RAISING THIS_IS_THE_SAME_WORKFLOW.
"Received event from the own workflow instance
ENDIF.
ENDIF.
ENDIF.
ENDIF.
CATCH CX_ROOT.
"Exception handling is cut here for a better overview only
"Attached coding file has full exception handling provided
ENDTRY.
ENDFUNCTION.
Enjoy Workflow.
Take care.
Any helpful comments are highly appreciated.
Florin Wach
Systems-Integration
SAP Business Workflow Senior-Expert
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
13 | |
11 | |
5 | |
5 | |
5 | |
4 | |
4 | |
3 | |
3 | |
3 |