Hi,
my name is Dieter Siebeck and I am quite new to the OpenXPKI project. I
am working with Oli Welter since nearly 3 months now, mainly on his
idea/draft of "paused workflows" (see his mail to the list from 2012-02-14).
Now I have published my first implementation of this feature. You can
see and download it here:
https://github.com/dsiebeck/openxpki/tree/feature%2Fworkflow
(this branch has been rebased on the devel branch 22 days ago.)
POD Documentation of most of the new methods is still missing, but I
wanted to publish the current state, to give other interested people the
opportunity to see and maybe correct the big picture.
Also missing are test files/scripts. I would appreciate some
recommendation, which place to look for the most up2date state-of-the
art testing files for this.
Now the idea of the implementation: basic need was to gain relatively
low-level control to the execution of workflows and activities. To
achieve this, the best (and maybe only) way seemed, to install an "own"
object of class OpenXPKI::Server::Workflow, which replaces (and of
course extends) the CPAN Workflow class (the file existed already, but
only for documentation purposes). Now its a matter of fact, that the
CPAN workflow Module isn't *really* designed for extendability. You have
no way to tell Workflow::Factory that you want "My::Workflow" instead of
"Workflow". And the Workflow class itself has not many access- or hook
points to control the execution of code.(for example, in method
Workflow::set(), the name of workflow-class is hardcoded as the one and
only class, which is allowed to use the private set-Method).
So I had to use some perl tricks to get around this limitations (if we
dont want to copy the hole Workflow-Module and extend it directly):
1) OpenXPKI::Workflow::Factory returns always an object of class
OpenXPKI::Server::Workflow. This is achieved via a friendly capturing
maneuver:
# OpenXPKI::Workflow::Factory
sub fetch_workflow {
my ( $self, $wf_type, $wf_id ) = @_;
my $wf = $self->SUPER::fetch_workflow($wf_type, $wf_id);
...
my $oxiWf = OpenXPKI::Server::Workflow->new($wf);
return $oxiWf;
#OpenXPKI::Server::Workflow
use base qw( Workflow )
sub new{
my ( $class, $BaseWorkflow ) = @_;
my $self = bless {}, $class;
#take over all properties from (original) base workflow
while ( my ( $key, $val ) = each %{$BaseWorkflow} ) {
$self->{$key} = $val;
}
...
return $self;
}
not the most beautiful code at all, but - thanks to perls open oop
structure - it works. OpenXPKI::Server::Workflow has all methods and
properties from the original workflow.
Another limitation: Workflow::Factory::fetch_workflow() uses only
certain data (two hardcoded keys) of the returned db data hash of the
persister.
Since there is no entry point/hook to alter this behaviour, a second
DB-fetch is needed to retrieve other data from the workflow-table (and
pass it to the workflow-object):
my $persister = $self->get_persister( $wf_config->{persister} );
my $wf_info = $persister->fetch_workflow($wf_id);
my $oxiWf = OpenXPKI::Server::Workflow->new($wf,$self,$wf_info);
So the full signature of the worklow constructor is:
my ( $class, $BaseWorkflow, $Factory, $wfData ) = @_;
(The reference of the factory is used to call the
Factory::save_workflow() method several time to store the current states)
2) OpenXPKI::Server::Workflow overrides the execute_action() - method.
As a wrapper method, it executes some code before and after
SUPER::execute_action(). Here is the place to determine/set the
appropriate "proc state" (which is a new db field and can have the values :
* init
* wakeup
* resume
* running
* manual
* finished
* pause
* exception
* retry_exceeded
The first 3 are not expected to see in "normal" circumstances - because
they are quite volatile by design. "running" will be set, before
SUPER::execute_action/Activity::run is called. After eexecution of one
or more Activities, either "manual" (waiting for interaction) or
"finished" will be set.
If an exception occurs, the proc state "exception" is set. Also the
message code (not translation) will be saved in WF context (key
"wf_exception")
The two states "pause" and "retry_exceeded" concern the new "pause" feature.
3) OpenXPKI::Server::Workflow::Activity has some new methods and utility
features:
first the pause() method: every derived concrete activity-class can now
(somewhere in its "run"-method call:
$self->pause('some cause description');
This will immediately end the execution of the "run"-method (this is
achieved via OpenXPKI::Server::Workflow::Pause, an "pseudo" exception
class) .
Additionally, $workflow->pause() is called, which does the hole work:
check the count of allowed retries, sets the "wake up" timestamp,
notifiy observers, write history entries etc. The workflow state remains
unchained (this is achieved via overriding the
Workflow::_get_next_state() method). If "max_retry" is exceeded, an
special exception will be thrown, which results in the special proc
state "retry_exceeded". The given "cause" of the pause will also be
stored in workflow context, key "wf_pause_msg".
For convenience, the Activity objects can now always "speak" to their
workflow via $self->{CURRENT_WORKFLOW}. Also I have a new utility method
"_get_wf_action_param($key)", which retrieves the param $key for the
current action in the xml-conf of current workflow.
4) Wake up: if an paused workflow is executed again (via command line,
the new "watcher" demon etc ), the proc state changes to "wakeup" and
the hook method Activity::wake_up() will be called. Afterwards,
proc-state changes to "running" and the normal execution begins.
5) Resume: if a workflow with proc state "exception" is executed again,
the proc state "resume" is set and the corresponing hook method is
called. Afterwards, proc-state changes to "running" and the normal
execution beginns.
6) The settings of "count try" and "resume_at" can be set/configured (in
this order)
manual set via $self->set_count_try/max_retry()
in action definition of the xml config of current workflow
in the xml config of the activity
Additionally, the retry-intervall can be set as second parameter of
$self->pause('cause', $retry_interval), which has the highest priority.
These are the main points of the implementation. Any existing Activity
class should run without any modifications and problems.
Of course, the workflow table has obtained some new fields:
workflow_proc_state, workflow_wakeup_at, workflow_count_try and
workflow_reap_at (the later corresponds to the "reap at"-Feature, which
is not implemented yet. Here's the full layout of my workflow table:
CREATE TABLE IF NOT EXISTS `workflow` (
`workflow_id` decimal(49,0) NOT NULL,
`pki_realm` varchar(255) DEFAULT NULL,
`workflow_type` varchar(255) DEFAULT NULL,
`workflow_version_id` decimal(49,0) DEFAULT NULL,
`workflow_state` varchar(255) DEFAULT NULL,
`workflow_last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP,
`workflow_proc_state` varchar(20) DEFAULT NULL,
`workflow_wakeup_at` timestamp NULL DEFAULT NULL,
`workflow_count_try` int(11) DEFAULT NULL,
`workflow_reap_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`workflow_id`)
);
Next steps are:
1. documentation
2. write some tests (suggestions welcome, see above)
3. setting the "reap at" timestamp (as proposed by oli)
4. implementing the "watcher" process, which runs in background and
periodically checks, if it finds some paused workflows to "wake up".
Any comments, suggestions and corrections are highly appreciated!
Another question: where would be the best place to put an explanation
like the one in this email here? In one perl class? Or is there an extra
place for such docs, which give an overview over the interplay of
several classes?
have a nice weekend,
Dieter
PS:
Since test files are missing, this is the way to test the feature
(relying on I18N_OPENXPKI_WF_TYPE_TESTING
(OpenXPKI::Server::Workflow::Activity::WorkflowTest):
openxpkictl restart --debug
OpenXPKI::Server::Workflow:128,OpenXPKI::Server::Workflow::Activity:20
openxpkicmd I18N_OPENXPKI_WF_TYPE_TESTING (expected output: "Workflow
created (ID: xxxxx), State: STEP2")
openxpkicmd --wfid xxxxx --param action=pause --wfaction
I18N_OPENXPKI_WF_ACTION_WORKFLOWTEST I18N_OPENXPKI_WF_TYPE_TESTING
(expected output: "New Workflow State: STEP2")
execute last line more than 4 times, and the "max retry exceeded" event
occurs.
Alternatively, you can set "--param action=crash" to provoke an exception.
to continue normally (till SUCCES) you need to give an arbitrary value
for "action", e.g. "abc" (due to some limitation in the parameter
parsing here)
------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and
threat landscape has changed and how IT managers can respond. Discussions
will include endpoint security, mobile security and the latest in malware
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
OpenXPKI-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openxpki-devel