h2. Business Process Administration

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":participants.thtml 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":http://jmettraux.wordpress.com/2007/06/25/operating-system-for-business-processes/.

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).

#:code#ruby#
    #
    # 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
#:code#

This page looks at

* "what's going on in the engine ?":#ongoing
* "pausing and resuming processes":#pausing_processes
* "cancelling processes":#cancelling_processes
* "cancelling expressions":#cancelling_expressions
* "replaying after errors":#replay

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).


h3. <a name="ongoing"></a>what's going on in there ?

There are 3 methods for questioning the engine about what's going on. The last one is certainly the most useful.

* "engine.list_processes(consider_subprocesses=false, wfid_prefix=nil)":#listing_processes - listing the top expressions of the processes currently in the engine
* "engine.get_process_stack(wfid)":#process_stack - lists all the expressions belonging to a process instance
* "engine.list_process_status(wfid_prefix=nil)":#process_status - this is by far the most interesting of the three methods, it returns a hash of process statuses, one per instance, complete with currenlty executing expressions and potential errors
* "engine.process_status(wfid)":#process_status - is like process_statuses but returns directly the process status of the process instance (given by its wfid (workflow instance id))


h4. <a name="listing_processes"></a>listing processes

(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).

#:code#ruby#
engine.list_processes.each do |flow_expression|
    puts "- #{flow_expression.fei}"
end
#:code#

will produce something like :

<pre>
- (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)
</pre>

The flow expression ids may be used when "cancelling":#cancelling_processes whole processes.


h4. <a name="process_stack"></a>viewing a process stack

#:code#ruby#
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
#:code#

will output something like :

<pre>
* (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)
</pre>


h4. <a name="process_status"></a>process status

The engine has a Engine.process_statuses(wfid_prefix=nil) method.

#:code#ruby#
puts engine.process_statuses.to_s
#:code#

will yield something like :

<pre>
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
</pre>

'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

#:code#ruby#
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
#:code#

The method <tt>engine.process_status(wfid)</tt> will directly yield the process status for a given process instance :

#:code#ruby#
puts engine.process_status("20070706-geramoke").to_s
#:code#

View the "ProcessStatus":rdoc/classes/OpenWFE/ProcessStatus.html class rdoc.


h3. <a name="pausing_processes"></a>pausing and resuming processes

Since OpenWFEru 0.9.14, it's possible to pause and resume processes individually. The engine features a <tt>pause_process(wfid)</tt> and a <tt>resume_process(wfid)</tt> methods.

#:code#ruby#
engine.pause_process('20070706-geramoke')
puts engine.process_statuses.to_s
#:code#

will result in something that looks like :

<pre>
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
</pre>

Cancelling a paused process instance will cancel it altogether.

To resume a paused process instance :

#:code#ruby#
engine.resume_process('20070706-geramoke')
#:code#

Operations on paused processes result in errors called 'paused errors'. The <tt>resume_process()</tt> 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.


h3. <a name="cancelling_processes"></a>cancelling processes

If you know the 'wfid' of a process instance, it is easy to cancel it :

#:code#ruby#
engine.cancel_process(wfid)
#:code#

This is equivalent to the "cancel-process":expressions.html#exp_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).


h3. <a name="cancelling_expressions"></a>cancelling expressions

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. 

#:code#ruby#
engine.cancel_expression(flow_expression)
    # or
engine.cancel_expression(fei)
#:code#

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":expressions.html#exp_undo expression is worth a look.


h3. <a name="replay"></a>replaying after errors

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.


h4. the (replay) process

An error resolution process could be summarized as :

# spot and fetch the error
# analyze it and fix the root cause
# replay (at the error)


h4. spotting

Process execution will sometimes result in an error. As already seen ProcessStatus (via engine.process_statuses()) yields information about the errors affecting process instances.

#:code#ruby#
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
#:code#

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 :

#:code#ruby#
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
#:code#


h4. fixing

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":http://www.google.com is your friend).

#:code#ruby#
engine.get_error_journal.get_error_log(my_wfid).each do |error|
    puts
    puts " --- the error stack trace ---"
    puts
    puts error.stacktrace
    puts
end
#:code#


h4. replay 

With a ProcessError instance at hand, this is possible :

#:code#ruby#
engine.replay_at_error error
#:code#

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.

