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 my $nochanges = shift; # no guarantees, yet...
18 $class = ref($class) || $class;
20 my $standalone = $editor ? 0 : 1;
21 $editor ||= new_editor();
23 if (ref($id) && ref($id) eq $class) {
24 $id->environment->{EventProcessor} = $id
25 if ($id->environment->{complete}); # in case it came over an opensrf tube
26 $id->editor( $editor );
27 $id->standalone( $standalone );
31 my $self = bless { id => $id, editor => $editor, standalone => $standalone, nochanges => $nochanges } => $class;
40 return $self if ($self->event);
43 $self->environment( {} );
46 $log->error("No Event ID provided");
47 die "No Event ID provided";
50 return $self if (!$self->id);
52 if ($self->standalone) {
53 $self->editor->xact_begin || return undef;
57 $self->editor->retrieve_action_trigger_event([
61 atev => [ qw/event_def/ ],
62 atevdef => [ qw/hook env params/ ]
68 if ($self->standalone) {
69 $self->editor->xact_rollback || return undef;
72 $self->user_data(OpenSRF::Utils::JSON->JSON2perl( $self->event->user_data ))
73 if (defined( $self->event->user_data ));
75 if ($self->event->state eq 'valid') {
77 } elsif ($self->event->state eq 'invalid') {
79 } elsif ($self->event->state eq 'reacting') {
81 } elsif ($self->event->state eq 'reacted') {
84 } elsif ($self->event->state eq 'cleaning') {
87 } elsif ($self->event->state eq 'complete') {
91 } elsif ($self->event->state eq 'error') {
97 unless ($self->nochanges) {
98 $self->update_state('found') || die 'Unable to update event state';
101 my $class = $self->_fm_class_by_hint( $self->event->event_def->hook->core_type );
103 my $meth = "retrieve_" . $class;
104 $meth =~ s/Fieldmapper:://;
107 if ($self->standalone) {
108 $self->editor->xact_begin || return undef;
111 $self->target( $self->editor->$meth( $self->event->target ) );
113 if ($self->standalone) {
114 $self->editor->xact_rollback || return undef;
117 unless ($self->target) {
118 $self->update_state('invalid') unless $self->nochanges;
127 my $env = shift || $self->environment;
129 return $self if (defined $self->cleanedup);
131 if (defined $self->reacted) {
132 $self->update_state( 'cleaning') || die 'Unable to update event state';
134 my $cleanup = $self->reacted ? $self->event->event_def->cleanup_success : $self->event->event_def->cleanup_failure;
137 OpenILS::Application::Trigger::ModRunner::Cleanup
138 ->new( $cleanup, $env)
146 $log->error("Event cleanup failed with ". shift() );
147 $self->update_state( 'error' ) || die 'Unable to update event state';
150 if ($self->cleanedup) {
151 $self->update_state( 'complete' ) || die 'Unable to update event state';
153 $self->update_state( 'error' ) || die 'Unable to update event state';
157 $self->{cleanedup} = undef;
164 my $env = shift || $self->environment;
166 return $self if (defined $self->reacted);
169 if ($self->event->event_def->group_field) { # can't react individually to a grouped definition
170 $self->{reacted} = undef;
172 $self->update_state( 'reacting') || die 'Unable to update event state';
174 my $reactor = OpenILS::Application::Trigger::ModRunner::Reactor->new(
175 $self->event->event_def->reactor,
179 $self->reacted( $reactor->run->final_result);
181 if ($env->{usr_message}{usr} && $env->{usr_message}{template}) {
182 my $message_template_output =
183 $reactor->pass('ProcessMessage')->run->final_result;
185 if ($message_template_output) {
186 my $usr_message = Fieldmapper::actor::usr_message->new;
187 $usr_message->title( $self->event->event_def->message_title || $self->event->event_def->name );
188 $usr_message->message( $message_template_output );
189 $usr_message->usr( $env->{usr_message}{usr}->id );
190 $usr_message->sending_lib( $env->{usr_message}{sending_lib}->id );
192 if ($self->editor->xact_begin) {
193 if ($self->editor->create_actor_usr_message( $usr_message )) {
194 $self->editor->xact_commit;
196 $self->editor->xact_rollback;
203 $log->error("Event reacting failed with ". shift() );
204 $self->update_state( 'error' ) || die 'Unable to update event state';
207 if (defined $self->reacted) {
208 $self->update_state( 'reacted' ) || die 'Unable to update event state';
210 $self->update_state( 'error' ) || die 'Unable to update event state';
214 $self->{reacted} = undef;
222 return $self if (defined $self->valid);
224 if ($self->build_environment->environment->{complete}) {
225 $self->update_state( 'validating') || die 'Unable to update event state';
228 OpenILS::Application::Trigger::ModRunner::Validator
229 ->new( $self->event->event_def->validator, $self->environment )
234 $log->error("Event validation failed with ". shift() );
235 $self->update_state( 'error' ) || die 'Unable to update event state';
238 if (defined $self->valid) {
240 $self->update_state( 'valid' ) || die 'Unable to update event state';
242 $self->update_state( 'invalid' ) || die 'Unable to update event state';
245 $self->update_state( 'error' ) || die 'Unable to update event state';
248 $self->{valid} = undef
254 sub revalidate_test {
257 if ($self->build_environment->environment->{complete}) {
260 OpenILS::Application::Trigger::ModRunner::Validator->new(
261 $self->event->event_def->validator,
266 $log->error("Event revalidation failed with ". shift());
269 return 1 if defined $self->valid and $self->valid;
274 "revalidate: could not build environment for event " .
282 return undef unless (ref $self);
285 $self->{cleanedup} = $c if (defined $c);
286 return $self->{cleanedup};
291 return undef unless (ref $self);
294 $self->{user_data} = $r if (defined $r);
295 return $self->{user_data};
300 return undef unless (ref $self);
303 $self->{reacted} = $r if (defined $r);
304 return $self->{reacted};
309 return undef unless (ref $self);
312 $self->{valid} = $v if (defined $v);
313 return $self->{valid};
318 return undef unless (ref $self);
321 $self->{event} = $e if (defined $e);
322 return $self->{event};
327 return undef unless (ref $self);
330 $self->{id} = $i if (defined $i);
336 return undef unless (ref $self);
339 $self->{environment} = $e if (defined $e);
340 return $self->{environment};
345 return undef unless (ref $self);
348 $self->{editor} = $e if (defined $e);
349 return $self->{editor};
353 # no guarantees, yet.
355 return undef unless (ref $self);
358 $self->{nochanges} = $e if (defined $e);
359 return $self->{nochanges};
364 return undef unless (ref $self);
366 die 'Cannot unfind a reacted event' if (defined $self->reacted);
368 $self->update_state( 'pending' ) || die 'Unable to update event state';
370 $self->{event} = undef;
371 $self->{environment} = undef;
377 return undef unless (ref $self);
380 $self->{target} = $t if (defined $t);
381 return $self->{target};
386 return undef unless (ref $self);
389 $self->{standalone} = $t if (defined $t);
390 return $self->{standalone};
395 return undef unless ($self && ref $self);
398 return undef unless ($state);
402 if ($self->standalone) {
403 $self->editor->xact_begin || return undef;
406 my $e = $self->editor->retrieve_action_trigger_event( $self->id );
408 $log->error( "Could not retrieve object ".$self->id." for update" ) if (!$e);
412 if ($fields && ref($fields)) {
413 $e->$_($$fields{$_}) for (keys %$fields);
416 $log->info( "Retrieved object ".$self->id." for update" );
417 $e->start_time( 'now' ) unless $e->start_time;
418 $e->update_time( 'now' );
419 $e->update_process( $$ );
422 $e->clear_start_time() if ($e->state eq 'pending');
423 $e->complete_time( 'now' ) if ($e->state eq 'complete');
425 my $ok = $self->editor->update_action_trigger_event( $e );
427 $self->editor->xact_rollback if ($self->standalone);
428 $log->error( "Update of event ".$self->id." failed" );
431 $e = $self->editor->data;
432 $e = $self->editor->retrieve_action_trigger_event( $e ) if (!ref($e));
434 $log->error( "Update of event ".$self->id." did not return an object" );
437 $log->info( "Update of event ".$e->id." suceeded" );
438 $ok = $self->editor->xact_commit if ($self->standalone);
442 $self->event->start_time( $e->start_time );
443 $self->event->update_time( $e->update_time );
444 $self->event->update_process( $e->update_process );
445 $self->event->state( $e->state );
451 my $current_environment;
453 sub build_environment {
455 return $self if ($self->environment->{complete});
457 $self->update_state( 'collecting') || die 'Unable to update event state';
461 my $compartment = new Safe;
462 $compartment->permit(':default','require','dofile','caller');
463 $compartment->share('$current_environment');
465 $self->environment->{EventProcessor} = $self;
466 $self->environment->{target} = $self->target;
467 $self->environment->{event} = $self->event;
468 $self->environment->{template} = $self->event->event_def->template;
469 $self->environment->{usr_message}{template} = $self->event->event_def->message_template;
470 $self->environment->{usr_message}{title} = $self->event->event_def->message_title;
471 $self->environment->{user_data} = $self->user_data;
473 $current_environment = $self->environment;
475 $self->environment->{params}{ $_->param } = $compartment->reval($_->value) for ( @{$self->event->event_def->params} );
477 for my $e ( @{$self->event->event_def->env} ) {
479 @path = split(/\./, $e->path) if ($e->path);
480 @label = split(/\./, $e->label) if ($e->label);
482 $self->_object_by_path( $self->target, $e->collector, \@label, \@path );
485 if ($self->event->event_def->group_field) {
486 my @group_path = split(/\./, $self->event->event_def->group_field);
487 pop(@group_path); # the last part is a field, should not get fleshed
488 my $group_object = $self->_object_by_path( $self->target, undef, [], \@group_path ) if (@group_path);
491 if ($self->event->event_def->message_usr_path and $self->environment->{usr_message}{template}) {
492 my @usr_path = split(/\./, $self->event->event_def->message_usr_path);
493 $self->_object_by_path( $self->target, undef, [qw/usr_message usr/], \@usr_path );
495 if ($self->event->event_def->message_library_path) {
496 my @library_path = split(/\./, $self->event->event_def->message_library_path);
497 $self->_object_by_path( $self->target, undef, [qw/usr_message sending_lib/], \@library_path );
499 $self->_object_by_path( $self->event->event_def, undef, [qw/usr_message sending_lib/], ['owner'] );
503 $self->environment->{complete} = 1;
505 $log->error( shift() );
506 $self->update_state( 'error' ) || die 'Unable to update event state';
509 if ($self->environment->{complete}) {
510 $self->update_state( 'collected' ) || die 'Unable to update event state';
512 $self->update_state( 'error' ) || die 'Unable to update event state';
518 sub _fm_class_by_hint {
523 OpenILS::Application->publish_fieldmapper->{$_}->{hint} eq $hint
524 } keys %{ OpenILS::Application->publish_fieldmapper };
529 my %_object_by_path_cache = ();
530 sub ClearObjectCache {
531 for my $did ( keys %_object_by_path_cache ) {
532 my $phash = $_object_by_path_cache{$did};
533 for my $path ( keys %$phash ) {
534 my $shash = $$phash{$path};
535 for my $fhint ( keys %$shash ) {
536 my $hhash = $$shash{$fhint};
537 for my $step ( keys %$hhash ) {
538 my $fhash = $$hhash{$step};
539 for my $ffield ( keys %$fhash ) {
540 my $lhash = $$fhash{$ffield};
541 for my $lfield ( keys %$lhash ) {
542 delete $$lhash{$lfield};
544 delete $$fhash{$ffield};
546 delete $$hhash{$step};
548 delete $$shash{$fhint};
550 delete $$phash{$path};
552 delete $_object_by_path_cache{$did};
556 sub _object_by_path {
559 my $collector = shift;
567 $ed = new_editor(xact=>1);
571 my $step = shift(@$path);
573 my $fhint = OpenILS::Application->publish_fieldmapper->{$context->class_name}{links}{$step}{class};
574 my $fclass = $self->_fm_class_by_hint( $fhint );
576 OpenSRF::EX::ERROR->throw(
577 "$step is not a field on ".$context->class_name." Please repair the environment.")
580 my $ffield = OpenILS::Application->publish_fieldmapper->{$context->class_name}{links}{$step}{key};
581 my $rtype = OpenILS::Application->publish_fieldmapper->{$context->class_name}{links}{$step}{reltype};
583 my $meth = 'retrieve_';
586 if ($rtype ne 'has_a') {
589 $lfield = $context->Identity;
593 $meth =~ s/Fieldmapper:://;
596 my $obj = $context->$step();
599 sprintf "_object_by_path(): meth=%s, obj=%s, multi=%s, step=%s, lfield=%s",
600 map {defined($_)? $_ : ''} ($meth, $obj, $multi, $step, $lfield)
605 my $lval = $context->$lfield();
609 my $def_id = $self->event->event_def->id;
610 my $str_path = join('.', @$path);
612 my @params = (($multi) ? { $ffield => $lval } : $lval);
613 @params = ([@params], {substream => 1}) if $meth =~ /^search/;
615 $obj = $_object_by_path_cache{$def_id}{$str_path}{$fhint}{$step}{$ffield}{$lval} ||
618 OpenILS::Application->publish_fieldmapper->{$fclass}{controller}
619 }) ? $ed : ($red ||= new_rstore_editor(xact=>1))
622 $_object_by_path_cache{$def_id}{$str_path}{$fhint}{$step}{$ffield}{$lval} ||= $obj;
630 $obj_list = [$obj] if ($obj);
636 my @path_clone = @$path;
637 $self->_object_by_path( $_, $collector, $label, \@path_clone, $ed, $red );
640 $obj = $$obj_list[0] if (!$multi || $rtype eq 'might_have');
641 $context->$step( $obj ) if ($obj && (!$label || !@$label));
646 my $obj_list = [$obj] if ($obj && !$multi);
647 $obj_list = $obj if ($multi);
650 for my $o ( @$obj_list ) {
652 OpenILS::Application::Trigger::ModRunner::Collector
653 ->new( $collector, $o )
659 $obj = $new_obj_list[0];
661 $obj = \@new_obj_list;
665 if ($label && @$label) {
666 my $node = $self->environment;
667 my $i = 0; my $max = scalar(@$label);
668 for (; $i < $max; $i++) {
669 my $part = $$label[$i];
670 $$node{$part} ||= {};
671 $node = $$node{$part};
673 $$node{$$label[-1]} = $obj;
675 $obj = $$obj[0] if $rtype eq 'might_have';
676 $context->$step( $obj ) if ($obj);
682 $red->rollback if $red;