Disclaimer : this page does not describe a fancy user interface for business process administration in OpenWFEru. Instead it describes the base methods for questioning the engine and managing the processes within it.
The documentation for [un]registering participants in the engine is in the participants page.
The things described here play with the engine (and its expression pool). Throughout the Ruby code examples, it is assumed that the engine is accessible from the variable named “engine”.
Workflow instances (process instances) are usually designated with a workflow instance id, which is abbreviated to wfid. One could use pid and that would really do the process engine == operating system.
Expression instances are atomic pieces of running Process instances. They are designated with a unique FlowExpressionId. Workitems are tagged with the FlowExpressionId of the expression they are currently in (most likely a ParticipantExpression).
#
# launching a process :
#
fei = engine.launch(MyProcessDefinition)
# or
fei = engine.launch(launchItem)
puts "launched process '#{fei.workflow_instance_id}'"
#
# from within a participant :
#
fei = workitem.fei
# or
fei = workitem.last_expression_id
#
# getting the process instance id (wfid) from a FlowExpressionId :
#
wfid = fei.workflow_instance_id
# or
wfid = fei.wfid
This page looks at
What’s missing for now : pausing an engine and replacing a segment of a business process on the fly.
(Maybe this page will be split in future releases, don’t link to much to it directly).
There are 3 methods for questioning the engine about what’s going on. The last one is certainly the most useful.
(listing their top/root expressions)
list_processes returns a list of FlowExpression instances. These are the root expressions of all the processes currently in the engine (thus all are ‘process-definition’ expressions).
engine.list_processes.each do |flow_expression|
puts "- #{flow_expression.fei}"
end
will produce something like :
- (fei 0.9.12 engine/engine field:__definition Def 59 20070708-hanakegeyo process-definition 0) - (fei 0.9.12 engine/engine field:__definition Def 59 20070708-hanamujehi process-definition 0) - (fei 0.9.12 engine/engine field:__definition Def59b 0 20070708-hanenahebo process-definition 0)
The flow expression ids may be used when cancelling whole processes.
class SampleDef0 < ProcessDefinition
sequence do
participant :alice
concurrence do
participant :bob
participant :charly
end
participant :diane
end
end
li = OpenWFE::LaunchItem.new(SampleDef0)
li.customer_name = "Acme Ltd"
fei = engine.launch(li)
#...
puts engine.get_process_stack fei.wfid
will output something like :
* (fei Test 0 20070709-gigosedeto 0.0.1 sequence) `--p--> (fei Test 0 20070709-gigosedeto 0.0 sequence) `--e--> (fei Test 0 20070709-gigosedeto 0 environment) `--c--> (fei Test 0 20070709-gigosedeto 0.0.1.0 print) `--c--> (fei Test 0 20070709-gigosedeto 0.0.1.1 sleep) `--c--> (fei Test 0 20070709-gigosedeto 0.0.1.2 print) * (fei Test 0 20070709-gigosedeto 0.0 sequence) `--p--> (fei Test 0 20070709-gigosedeto 0 process-definition) `--e--> (fei Test 0 20070709-gigosedeto 0 environment) `--c--> (fei Test 0 20070709-gigosedeto 0.0.0 print) `--c--> (fei Test 0 20070709-gigosedeto 0.0.1 sequence) `--c--> (fei Test 0 20070709-gigosedeto 0.0.2 print) * (fei Test 0 20070709-gigosedeto 0.0.1.1 sleep) `--p--> (fei Test 0 20070709-gigosedeto 0.0.1 sequence) `--e--> (fei Test 0 20070709-gigosedeto 0 environment) `--c--> >1s< * (fei Test 0 20070709-gigosedeto 0 process-definition) `--e--> (fei Test 0 20070709-gigosedeto 0 environment) `--c--> (fei Test 0 20070709-gigosedeto 0.0 sequence)
The engine has a Engine.process_statuses(wfid_prefix=nil) method.
puts engine.process_statuses.to_s
will yield something like :
process_id | name | rev | brn | err | paused? ------------------+-------------------+---------+-----+-----+--------- 20070706-geramoke | Def | 59.1 | 1 | 1 | false 20070708-giramute | Peer Review | 0.9.1 | 2 | 0 | false
‘brn’ : means ‘branches’, for a process without concurrence, will indicate ‘1’, else it will indicate how many concurrent branches there are in the process instance.
‘err’ : indicates how many errors affect the the process instance.
The method process_statuses accepts an optional ‘wfid_prefix’ parameter
puts engine.process_statuses("2007").to_s
#
# only the processes of 2007
puts engine.process_statuses("200707").to_s
#
# only the processes of July 2007
The method engine.process_status(wfid) will directly yield the process status for a given process instance :
puts engine.process_status("20070706-geramoke").to_s
View the ProcessStatus class rdoc.
Since OpenWFEru 0.9.14, it’s possible to pause and resume processes individually. The engine features a pause_process(wfid) and a resume_process(wfid) methods.
engine.pause_process('20070706-geramoke')
puts engine.process_statuses.to_s
will result in something that looks like :
process_id | name | rev | brn | err | paused? ------------------+-------------------+---------+-----+-----+--------- 20070706-geramoke | Def | 59.1 | 1 | 0 | true 20070708-giramute | Peer Review | 0.9.1 | 2 | 0 | false
Cancelling a paused process instance will cancel it altogether.
To resume a paused process instance :
engine.resume_process('20070706-geramoke')
Operations on paused processes result in errors called ‘paused errors’. The resume_process() call takes care of replaying these particular errors so that the process instance is resumed in a consistent state. Those paused errors will nevertheless appear in the process status error count.
If you know the ‘wfid’ of a process instance, it is easy to cancel it :
engine.cancel_process(wfid)
This is equivalent to the cancel-process of the OpenWFEru process definition language.
Cancelling a process instance will remove all its expressions from the engine. Cancelled participant expressions will send ‘cancel items’ to the participant implementation (that should be able to handle them).
Cancelling a process is easy to understand : bang, the whole process instance is gone. Cancelling a process amounts in fact to cancelling the root expression of a process instance.
It’s possible to cancel parts of a process instance.
engine.cancel_expression(flow_expression)
# or
engine.cancel_expression(fei)
Cancelling an expression will cancel the expression itself and all its children. thus cancelling a ‘sequence’ or a ‘concurrence’ expression will cut a whole segment away.
For cancelling expression from the process definition itself, the undo expression is worth a look.
It’s sometimes necessary to detect and fix issues in a process execution.
The usual issue is the non-responding participant, if the participant is for example a web service, it may be down when the process instance required it and an error occured, blocking the flow. It possible to unblock the process by ‘replaying at the error’.
The classical replay process is described in the following paragraphs.
An error resolution process could be summarized as :
Process execution will sometimes result in an error. As already seen ProcessStatus (via engine.process_statuses()) yields information about the errors affecting process instances.
if engine.process_status(my_wfid).errors.size > 0
puts "problem with process instance '#{my_wfid}'"
end
#
# the longer version (with error display) :
#
status = statuses.process_status(my_wfid)
if status.errors.size > 0
puts "problem with process instance '#{my_wfid}'"
status.errors.each do |error|
puts
puts error.to_s
end
end
Error tracking is done via a service of the engine named the ‘error journal’. The method process_statuses() consults this service when called, but it’s possible to query it directly :
if engine.get_error_journal.has_errors?(my_wfid)
puts "problem with process instance '#{my_wfid}'"
engine.get_error_journal.get_error_log(my_wfid).each do |error|
puts
puts error.to_s
end
end
The cause of the error might be internal to the engine (missing or misnamed participant, OpenWFEru bug, ...) or external (resource behind participant not available, ...).
In any case, a careful reading of the error trace is indispensable, the cause of the error is stated clearly and explicitely there 99% of the time. (and Google is your friend).
engine.get_error_journal.get_error_log(my_wfid).each do |error|
puts
puts " --- the error stack trace ---"
puts
puts error.stacktrace
puts
end
With a ProcessError instance at hand, this is possible :
engine.replay_at_error error
This method call will take care of removing the error from the journal before replaying it. Only ‘active’ errors will remain in the error journal.