1 package OpenILS::Application::Trigger::Event;
2 use strict; use warnings;
3 use OpenSRF::EX qw/:try/;
4 use OpenSRF::Utils::JSON;
5 use OpenSRF::Utils::Logger qw/$logger/;
6 use OpenILS::Utils::Fieldmapper;
7 use OpenILS::Utils::CStoreEditor q/:funcs/;
8 use OpenILS::Application::Trigger::ModRunner;
11 my $log = 'OpenSRF::Utils::Logger';
17 $class = ref($class) || $class;
19 my $standalone = $editor ? 0 : 1;
20 $editor ||= new_editor();
22 if (ref($id) && ref($id) eq $class) {
23 $id->environment->{EventProcessor} = $id
24 if ($id->environment->{complete}); # in case it came over an opensrf tube
25 $id->editor( $editor );
26 $id->standalone( $standalone );
30 my $self = bless { id => $id, editor => $editor, standalone => $standalone } => $class;
39 return $self if ($self->event);
42 $self->environment( {} );
45 $log->error("No Event ID provided");
46 die "No Event ID provided";
49 return $self if (!$self->id);
51 if ($self->standalone) {
52 $self->editor->xact_begin || return undef;
56 $self->editor->retrieve_action_trigger_event([
60 atev => [ qw/event_def/ ],
61 atevdef => [ qw/hook env params/ ]
67 if ($self->standalone) {
68 $self->editor->xact_rollback || return undef;
71 $self->user_data(OpenSRF::Utils::JSON->JSON2perl( $self->event->user_data ))
72 if (defined( $self->event->user_data ));
74 if ($self->event->state eq 'valid') {
76 } elsif ($self->event->state eq 'invalid') {
78 } elsif ($self->event->state eq 'reacting') {
80 } elsif ($self->event->state eq 'reacted') {
83 } elsif ($self->event->state eq 'cleaning') {
86 } elsif ($self->event->state eq 'complete') {
90 } elsif ($self->event->state eq 'error') {
97 $self->update_state('found') || die 'Unable to update event state';
99 my $class = $self->_fm_class_by_hint( $self->event->event_def->hook->core_type );
101 my $meth = "retrieve_" . $class;
102 $meth =~ s/Fieldmapper:://;
105 if ($self->standalone) {
106 $self->editor->xact_begin || return undef;
109 $self->target( $self->editor->$meth( $self->event->target ) );
111 if ($self->standalone) {
112 $self->editor->xact_rollback || return undef;
115 unless($self->target) {
116 $self->update_state('invalid');
125 my $env = shift || $self->environment;
127 return $self if (defined $self->cleanedup);
129 if (defined $self->reacted) {
130 $self->update_state( 'cleaning') || die 'Unable to update event state';
132 my $cleanup = $self->reacted ? $self->event->event_def->cleanup_success : $self->event->event_def->cleanup_failure;
135 OpenILS::Application::Trigger::ModRunner::Cleanup
136 ->new( $cleanup, $env)
144 $log->error("Event cleanup failed with ". shift() );
145 $self->update_state( 'error' ) || die 'Unable to update event state';
148 if ($self->cleanedup) {
149 $self->update_state( 'complete' ) || die 'Unable to update event state';
151 $self->update_state( 'error' ) || die 'Unable to update event state';
155 $self->{cleanedup} = undef;
162 my $env = shift || $self->environment;
164 return $self if (defined $self->reacted);
167 if ($self->event->event_def->group_field) { # can't react individually to a grouped definition
168 $self->{reacted} = undef;
170 $self->update_state( 'reacting') || die 'Unable to update event state';
173 OpenILS::Application::Trigger::ModRunner::Reactor
174 ->new( $self->event->event_def->reactor, $env )
179 $log->error("Event reacting failed with ". shift() );
180 $self->update_state( 'error' ) || die 'Unable to update event state';
183 if (defined $self->reacted) {
184 $self->update_state( 'reacted' ) || die 'Unable to update event state';
186 $self->update_state( 'error' ) || die 'Unable to update event state';
190 $self->{reacted} = undef;
198 return $self if (defined $self->valid);
200 if ($self->build_environment->environment->{complete}) {
201 $self->update_state( 'validating') || die 'Unable to update event state';
204 OpenILS::Application::Trigger::ModRunner::Validator
205 ->new( $self->event->event_def->validator, $self->environment )
210 $log->error("Event validation failed with ". shift() );
211 $self->update_state( 'error' ) || die 'Unable to update event state';
214 if (defined $self->valid) {
216 $self->update_state( 'valid' ) || die 'Unable to update event state';
218 $self->update_state( 'invalid' ) || die 'Unable to update event state';
221 $self->update_state( 'error' ) || die 'Unable to update event state';
224 $self->{valid} = undef
230 sub revalidate_test {
233 if ($self->build_environment->environment->{complete}) {
236 OpenILS::Application::Trigger::ModRunner::Validator->new(
237 $self->event->event_def->validator,
242 $log->error("Event revalidation failed with ". shift());
245 return 1 if defined $self->valid and $self->valid;
250 "revalidate: could not build environment for event " .
258 return undef unless (ref $self);
261 $self->{cleanedup} = $c if (defined $c);
262 return $self->{cleanedup};
267 return undef unless (ref $self);
270 $self->{user_data} = $r if (defined $r);
271 return $self->{user_data};
276 return undef unless (ref $self);
279 $self->{reacted} = $r if (defined $r);
280 return $self->{reacted};
285 return undef unless (ref $self);
288 $self->{valid} = $v if (defined $v);
289 return $self->{valid};
294 return undef unless (ref $self);
297 $self->{event} = $e if (defined $e);
298 return $self->{event};
303 return undef unless (ref $self);
306 $self->{id} = $i if (defined $i);
312 return undef unless (ref $self);
315 $self->{environment} = $e if (defined $e);
316 return $self->{environment};
321 return undef unless (ref $self);
324 $self->{editor} = $e if (defined $e);
325 return $self->{editor};
330 return undef unless (ref $self);
332 die 'Cannot unfind a reacted event' if (defined $self->reacted);
334 $self->update_state( 'pending' ) || die 'Unable to update event state';
336 $self->{event} = undef;
337 $self->{environment} = undef;
343 return undef unless (ref $self);
346 $self->{target} = $t if (defined $t);
347 return $self->{target};
352 return undef unless (ref $self);
355 $self->{standalone} = $t if (defined $t);
356 return $self->{standalone};
361 return undef unless ($self && ref $self);
364 return undef unless ($state);
368 if ($self->standalone) {
369 $self->editor->xact_begin || return undef;
372 my $e = $self->editor->retrieve_action_trigger_event( $self->id );
374 $log->error( "Could not retrieve object ".$self->id." for update" ) if (!$e);
378 if ($fields && ref($fields)) {
379 $e->$_($$fields{$_}) for (keys %$fields);
382 $log->info( "Retrieved object ".$self->id." for update" );
383 $e->start_time( 'now' ) unless $e->start_time;
384 $e->update_time( 'now' );
385 $e->update_process( $$ );
388 $e->clear_start_time() if ($e->state eq 'pending');
389 $e->complete_time( 'now' ) if ($e->state eq 'complete');
391 my $ok = $self->editor->update_action_trigger_event( $e );
393 $self->editor->xact_rollback if ($self->standalone);
394 $log->error( "Update of event ".$self->id." failed" );
397 $e = $self->editor->data;
398 $e = $self->editor->retrieve_action_trigger_event( $e ) if (!ref($e));
400 $log->error( "Update of event ".$self->id." did not return an object" );
403 $log->info( "Update of event ".$e->id." suceeded" );
404 $ok = $self->editor->xact_commit if ($self->standalone);
408 $self->event->start_time( $e->start_time );
409 $self->event->update_time( $e->update_time );
410 $self->event->update_process( $e->update_process );
411 $self->event->state( $e->state );
417 my $current_environment;
419 sub build_environment {
421 return $self if ($self->environment->{complete});
423 $self->update_state( 'collecting') || die 'Unable to update event state';
427 my $compartment = new Safe;
428 $compartment->permit(':default','require','dofile','caller');
429 $compartment->share('$current_environment');
431 $self->environment->{EventProcessor} = $self;
432 $self->environment->{target} = $self->target;
433 $self->environment->{event} = $self->event;
434 $self->environment->{template} = $self->event->event_def->template;
435 $self->environment->{user_data} = $self->user_data;
437 $current_environment = $self->environment;
439 $self->environment->{params}{ $_->param } = $compartment->reval($_->value) for ( @{$self->event->event_def->params} );
441 for my $e ( @{$self->event->event_def->env} ) {
443 @path = split(/\./, $e->path) if ($e->path);
444 @label = split(/\./, $e->label) if ($e->label);
446 $self->_object_by_path( $self->target, $e->collector, \@label, \@path );
449 if ($self->event->event_def->group_field) {
450 my @group_path = split(/\./, $self->event->event_def->group_field);
451 pop(@group_path); # the last part is a field, should not get fleshed
452 my $group_object = $self->_object_by_path( $self->target, undef, [], \@group_path ) if (@group_path);
455 $self->environment->{complete} = 1;
457 $log->error( shift() );
458 $self->update_state( 'error' ) || die 'Unable to update event state';
461 if ($self->environment->{complete}) {
462 $self->update_state( 'collected' ) || die 'Unable to update event state';
464 $self->update_state( 'error' ) || die 'Unable to update event state';
470 sub _fm_class_by_hint {
475 Fieldmapper->publish_fieldmapper->{$_}->{hint} eq $hint
476 } keys %{ Fieldmapper->publish_fieldmapper };
481 my %_object_by_path_cache = ();
482 sub ClearObjectCache {
483 for my $did ( keys %_object_by_path_cache ) {
484 my $phash = $_object_by_path_cache{$did};
485 for my $path ( keys %$phash ) {
486 my $shash = $$phash{$path};
487 for my $step ( keys %$shash ) {
488 my $fhash = $$shash{$step};
489 for my $ffield ( keys %$fhash ) {
490 my $lhash = $$fhash{$ffield};
491 for my $lfield ( keys %$lhash ) {
492 delete $$lhash{$lfield};
494 delete $$fhash{$ffield};
496 delete $$shash{$step};
498 delete $$phash{$path};
500 delete $_object_by_path_cache{$did};
504 sub _object_by_path {
507 my $collector = shift;
515 $ed = new_editor(xact=>1);
519 my $step = shift(@$path);
521 my $fhint = Fieldmapper->publish_fieldmapper->{$context->class_name}{links}{$step}{class};
522 my $fclass = $self->_fm_class_by_hint( $fhint );
524 OpenSRF::EX::ERROR->throw(
525 "$step is not a field on ".$context->class_name." Please repair the environment.")
528 my $ffield = Fieldmapper->publish_fieldmapper->{$context->class_name}{links}{$step}{key};
529 my $rtype = Fieldmapper->publish_fieldmapper->{$context->class_name}{links}{$step}{reltype};
531 my $meth = 'retrieve_';
534 if ($rtype ne 'has_a') {
537 $lfield = $context->Identity;
541 $meth =~ s/Fieldmapper:://;
544 my $obj = $context->$step();
547 sprintf "_object_by_path(): meth=%s, obj=%s, multi=%s, step=%s, lfield=%s",
548 map {defined($_)? $_ : ''} ($meth, $obj, $multi, $step, $lfield)
553 my $lval = $context->$lfield();
557 my $def_id = $self->event->event_def->id;
558 my $str_path = join('.', @$path);
560 $obj = $_object_by_path_cache{$def_id}{$str_path}{$step}{$ffield}{$lval} ||
563 Fieldmapper->publish_fieldmapper->{$fclass}{controller}
564 }) ? $ed : ($red ||= new_rstore_editor(xact=>1))
565 )->$meth( ($multi) ? { $ffield => $lval } : $lval);
567 $_object_by_path_cache{$def_id}{$str_path}{$step}{$ffield}{$lval} ||= $obj;
575 $obj_list = [$obj] if ($obj);
581 my @path_clone = @$path;
582 $self->_object_by_path( $_, $collector, $label, \@path_clone, $ed, $red );
585 $obj = $$obj_list[0] if (!$multi || $rtype eq 'might_have');
586 $context->$step( $obj ) if ($obj && (!$label || !@$label));
591 my $obj_list = [$obj] if ($obj && !$multi);
592 $obj_list = $obj if ($multi);
595 for my $o ( @$obj_list ) {
597 OpenILS::Application::Trigger::ModRunner::Collector
598 ->new( $collector, $o )
604 $obj = $new_obj_list[0];
606 $obj = \@new_obj_list;
610 if ($label && @$label) {
611 my $node = $self->environment;
612 my $i = 0; my $max = scalar(@$label);
613 for (; $i < $max; $i++) {
614 my $part = $$label[$i];
615 $$node{$part} ||= {};
616 $node = $$node{$part};
618 $$node{$$label[-1]} = $obj;
620 $obj = $$obj[0] if $rtype eq 'might_have';
621 $context->$step( $obj ) if ($obj);
627 $red->rollback if $red;