view release on metacpan or search on metacpan
bin/bpmengine-spawn view on Meta::CPAN
$BPM::Engine::Store::VERSION,
"$FindBin::Bin/../var"
);
print "DDL files created in ./var \n";
}
elsif ($deploy) {
$schema->deploy({ add_drop_table => $drop_tables });
# copy as test db
unlink './t/var/bpmengine.test.db' if -f './t/var/bpmengine.test.db';
die("Test db locked") if -f './t/var/bpmengine.test.db';
File::Copy::copy('./t/var/bpmengine.db', './t/var/bpmengine.test.db');
print "Schema deployed as ./t/var/bpmengine.db\n";
}
else {
pod2usage(1);
}
#$schema->storage->backup('./var') if $connect_info[0] =~ /SQLite/;
$schema->storage->disconnect if $connect_info[0] =~ /mysql/;
lib/BPM/Engine/ProcessRunner.pm view on Meta::CPAN
unless (@transitions) {
my $act_id =
$activity->activity_name
|| $activity->activity_uid
|| $activity->id;
throw_model error =>
"Model error: no outgoing transitions for activity '$act_id'";
}
my (@instances) = ();
my (@blocked) = ();
my ($stop_following, $fired_count) = (0, 0);
my ($otherwise, $exception) = ();
# evaluate efferent transitions
foreach my $transition (@transitions) {
if ( $transition->condition_type eq 'NONE'
|| $transition->condition_type eq 'CONDITION') {
my $t_instance;
unless ($stop_following) {
$t_instance =
lib/BPM/Engine/ProcessRunner.pm view on Meta::CPAN
}
if ($t_instance) {
push(@instances, [$transition, $t_instance]);
$fired_count++;
# only one transition in an XOR split can fire.
$stop_following++ if $activity->is_xor_split;
}
elsif ($activity->is_split) {
my $split = $instance->split
or die("No split for " . $activity->activity_uid);
$split->set_transition($transition->id, 'blocked');
push(@blocked, [$transition, $instance]);
}
}
elsif ($transition->condition_type eq 'OTHERWISE') {
$otherwise = $transition;
}
elsif ($transition->condition_type eq 'DEFAULTEXCEPTION'
|| $transition->condition_type eq 'EXCEPTION') {
$exception = $transition;
}
lib/BPM/Engine/ProcessRunner.pm view on Meta::CPAN
push(@instances, [$otherwise, $t_instance]);
}
else {
throw_runner error =>
"Execution of transition with 'Otherwise' condition failed";
}
}
elsif ($otherwise && $activity->is_split) {
my $split = $instance->split
or die("No join found for split " . $activity->activity_uid);
$split->set_transition($otherwise->id, 'blocked');
}
# activate successor activities
my $followed_back = 0;
foreach my $inst (@instances) {
$followed_back++ if $inst->[0]->is_back_edge;
my $r_instance = $inst->[1];
my $r_activity = $r_instance->activity;
$self->_enqueue_ai($r_activity, $r_instance);
}
# blocked paths may trigger downstream deferred activities which must now be
# resolved; signal deferred activity instances on other branches in the
# wf-net when paths were blocked and any transition downstream was followed
if (scalar(@blocked) && $followed_back != scalar @instances) {
$self->_signal_upstream_orjoins_if_in_split_branch(@blocked);
}
return;
}
sub _execute_transition {
my ($self, $transition, $from_instance, $run) = @_;
#XXX mitigate expensive debugging
my $tid = $transition->transition_uid || $transition->id || 'noid';
lib/BPM/Engine/ProcessRunner.pm view on Meta::CPAN
$self->debug("runner: _enqueue Pushing instance "
. $activity->activity_uid
. " to deferred queue");
$self->_defer_push([$activity, $instance]) unless $deferred;
}
}
sub _signal_upstream_orjoins_if_in_split_branch {
my ($self, @blocked) = @_;
my @deferred = $self->process_instance->activity_instances->deferred->all;
foreach my $instance (@deferred) {
$self->debug("runner: _run Pushing db instance "
. $instance->activity->activity_uid
. " to deferred queue");
my $graph = $self->graph;
foreach my $block (@blocked) {
my $tr = $block->[0];
my $ai = $block->[1];
my $a_to = $tr->to_activity;
if ($graph->is_reachable($a_to->id, $instance->activity->id)) {
$self->_defer_push([$instance->activity, $instance]);
}
}
}
}
lib/BPM/Engine/Store/Result/ActivityInstanceSplit.pm view on Meta::CPAN
__PACKAGE__->set_primary_key(qw/ split_id /);
__PACKAGE__->belongs_to(
activity_instance => 'BPM::Engine::Store::Result::ActivityInstance', 'token_id'
);
sub set_transition {
my ($self, $transition_id, $state) = @_;
die("Invalid split state '$state'") unless $state =~ /^(taken|blocked|joined)$/;
my $states = $self->states || {};
if($states->{$transition_id} && $state ne 'joined') {
die("Transition state '$state' already set in Join as '" .
$states->{$transition_id} . "'"
);
}
elsif(!$states->{$transition_id} && $state eq 'joined') {
die("State '$state' not previously taken for transition '$transition_id'");
}
lib/BPM/Engine/Store/ResultRole/ActivityInstanceJoin.pm view on Meta::CPAN
->resultset('ActivityInstance')->deferred({
activity_id => $self->activity->id,
process_instance_id => $self->process_instance_id,
tokenset => $self->tokenset,
});
my %deferred_trans = map { $_->transition_id => 1 } $deferred_states->all;
$deferred_trans{$self->transition_id} = 1;
# Each transition corresponds to either waiting for upstream,
# executed+deferred, blocked, the start of a new cycle or this ai's
# transition itself. Join should fire if there's no upstream activity left.
foreach my $transition($self->activity->transitions_in->all) {
next if($deferred_trans{$transition->id});
next if($transition->is_back_edge);
return 0 unless $self->_upstream_blocked($transition);
}
return 1;
}
# Search the transition's upstream subnet for active or blocked activity
# instances. Transition has not been applied yet, so either
# - still activity further upstream (last ai in process thread=active), or
# - split.path blocked for last completed ai in process thread
sub _upstream_blocked {
my ($self, $transition) = @_;
my $rs = $self->process_instance->activity_instances_rs({
tokenset => $self->tokenset,
})->active_or_completed;
my $split_blocked = sub {
my ($ai, $trans) = @_;
my $split = $ai->split || die("Inclusive split has no join attached");
$split->discard_changes;
if( $split->states->{$trans->id}
&& $split->states->{$trans->id} eq 'blocked') {
# no blocking if followed a backedge upstream (cyclic wf)
my @tids =
map { $_->id }
$ai->activity->transitions({ is_back_edge => 1 })->all;
if(scalar @tids) {
return 0 if $ai->next({ transition_id => [@tids] })->count;
}
return 1;
}
else {
lib/BPM/Engine/Store/ResultRole/ActivityInstanceJoin.pm view on Meta::CPAN
# no activity instances, traverse further upstream
if(!scalar @ai) {
foreach my $trans($upstream_act->transitions_in) {
next if $trans->is_back_edge;
my $src = $trans->from_activity;
unless($src->id == $self->activity->id) {
push(@act, [$src, $trans]);
}
}
}
# active or completed+blocked instances
else {
$seen++;
my %status = ();
foreach(@ai) {
$status{
$_->is_deferred ? 'deferred' :
($_->is_completed ? 'completed' : 'active')
}++;
}
die("Invalid db state for instances " . $upstream_act->activity_uid)
if($status{deferred} && ($status{active} || $status{completed}));
die("Invalid db state for instances " . $upstream_act->activity_uid)
if($status{active} && $status{active} > 1);
# active ai, may have come from split upstream
if($status{active}) {
return 0;
}
# completed, is_split, blocked transition path
elsif($status{completed} && scalar(keys %status) == 1) {
# OR-split should be blocked, XOR split missed this transition by definition
if($upstream_act->is_or_split) {
my $blocked = 0;
foreach my $ai(@ai) {
$blocked++ if &$split_blocked($ai, $down_trans);
}
die("OR split " . $upstream_act->activity_uid . " completed but not blocked")
unless $blocked;
}
elsif(!$upstream_act->is_xor_split) {
die("Not an OR/XOR split " . $upstream_act->activity_uid);
}
$block++;
}
else {
die("Wrong status");
}
}
t/04-run/01-transition.t view on Meta::CPAN
is($ai_B1->activity->activity_uid, 'ex4.B1','derive_and_accept results in B1');
# transition in joinA set to 'taken' since we're coming from a split
is($ai_A->split->states->{$t_A_B1->id}, 'taken', "Transition A-B1 state is 'taken'");
# after a split, the parent_token of the new ai is set to the split-ai
#is($ai_B1->parent_token_id, $ai_A->id, 'Parent matches');
#is($ai_B1->parent->id, $ai_A->id, 'Parent matches');
is($ai_B1->prev->id, $ai_A->id, 'Prev matches');
# join B1 should not fire, since although we didn't follow the path from A to B, A-B was not blocked either
# TODO: verify this is not a problem in runner!!
ok(!$ai_B1->is_enabled(), 'Join B1 should not fire');
# set A-B blocked, and see B1 enabled
$ai_A->split->discard_changes();
my $states = $ai_A->split->states;
$states->{$t_A_B->id} = 'blocked';
$ai_A->split->update({ states => $states })->discard_changes;
$ai_A->update({ completed => DateTime->now() });
ok($ai_B1->is_enabled(), 'Join B1 should fire');
is($ai_A->split->states->{$t_A_B->id}, 'blocked', "Transition A-B state is 'blocked'");
is($ai_A->split->states->{$t_A_B1->id}, 'taken', "Transition A-B1 state is 'taken'");
is($ai_B1->prev->split->states->{$t_A_B1->id}, 'taken', "Transition A-B1 state is 'taken'");
$ai_A->update({ completed => \'NULL' })->discard_changes;
delete $states->{$t_A_B->id};
$ai_A->split->update({ states => $states })->discard_changes;
ok(!$ai_B1->is_enabled(), 'Join B1 should not fire');
$ai_B1->update({ deferred => DateTime->now() });
t/04-run/01-transition.t view on Meta::CPAN
is($ai_B2->activity->activity_uid, 'ex4.B2','derive_and_accept results in B2');
# B2-D
$ai_B2->update({ completed => DateTime->now() });
ok($ai_B2->is_completed);
ok(my $ai_D = $tB2D->derive_and_accept_instance($ai_B2, { activity => $aD }, @args));
# Block B2-C
#ok(!$ai_C->is_enabled(), 'Join C should not fire'); # needs block when B2 completed
my $split = $ai_B2->split or die("No join found for split");
$split->set_transition($tB2C->id, 'blocked');
ok($ai_C->is_enabled(), 'Join C should fire');
$ai_C->update({ deferred => undef })->discard_changes;
$ai_C->fire_join;
ok(!$ai_D->is_enabled(), 'Join D should not fire');
$ai_D->update({ deferred => DateTime->now });
# C-D
$ai_C->update({ completed => DateTime->now() });
t/04-run/01-transition.t view on Meta::CPAN
# A-B
$ai_A->update({ completed => DateTime->now() }); # normally done in processrunner complete_activity
my $ai_B = $tAB->derive_and_accept_instance($ai_A, { activity => $aB }, @args);
# B-B1
$ai_B->update({ completed => DateTime->now() });
my $ai_B1b = $tBB1->derive_and_accept_instance($ai_B, { activity => $aB1 }, @args);
ok(!$ai_B1b->is_enabled(), 'Join B1 should not fire');
my $split = $ai_A->split or die("No join found for split");
$split->set_transition($tAB1->id, 'blocked');
ok($ai_B1b->is_enabled(), 'Join B1 should fire');
}
}
# patterns: 06-iteration.xpdl
#-----------------------------------------------
# WCP10: Arbitrary Cycles (nested-loops) - test tokensets
t/04-run/02-runner.t view on Meta::CPAN
complete_active(); # complete D
#test_state(completed => [qw/B C C D DC DC MC SM XOR XOR/], active => ['SM'], deferred => []);
test_state(completed => [qw/B C C D DC DC MC XOR XOR/], active => ['SM'], deferred => ['SM']);
complete_active(); # complete SM
test_state(completed => [qw/B C C D DC DC MC SM SM XOR XOR/], active => ['End'], deferred => []);
}
else {
$pi->attribute(deferred_choice => 'E');
complete_active(); # complete DC, follow DC-E
# path D was blocked, deferred SM now enabled (path D blocked in MC-localJoin), so should fire and execute
test_state(completed => [qw/B C C DC DC MC XOR XOR/], active => ['E','SM'], deferred => []);
complete_active(); # complete E+SM
test_state(completed => [qw/B C C DC DC E End MC SM XOR XOR/], active => ['End'], deferred => []);
complete_active(); # complete End
test_state(completed => [qw/B C C DC DC E End End MC SM XOR XOR/], active => [], deferred => []);
}
}