1 package OpenILS::Utils::ScriptRunner;
2 use strict; use warnings;
3 use OpenSRF::Utils::Logger qw(:logger);
4 use OpenSRF::EX qw(:try);
6 use JavaScript::SpiderMonkey;
9 use Time::HiRes qw/time/;
16 $class = ref($class) || $class;
17 $params{paths} ||= [];
18 $params{reset_count} ||= 0;
20 my $self = bless { file => $params{file},
21 libs => $params{libs},
22 reset_count => $params{reset_count},
24 _path => {%_paths} } => $class;
26 $self->add_path($_) for @{$params{paths}};
31 my( $self, $context ) = @_;
32 $self->{ctx} = $context if $context;
38 $self->context( new JavaScript::SpiderMonkey );
39 $self->context->init();
44 # eating our own dog food with insert
45 $self->insert(perl_print => sub { print "@_\n"; } );
46 $self->insert(perl_warn => sub { warn "@_\n"; } );
47 $self->insert(log_activity => sub { $logger->activity("script_runner: @_"); return 1;} );
48 $self->insert(log_error => sub { $logger->error("script_runner: @_"); return 1;} );
49 $self->insert(log_warn => sub { $logger->warn("script_runner: @_"); return 1;} );
50 $self->insert(log_info => sub { $logger->info("script_runner: @_"); return 1;} );
51 $self->insert(log_debug => sub { $logger->debug("script_runner: @_"); return 1;} );
52 $self->insert(log_internal => sub { $logger->internal("script_runner: @_"); return 1;} );
53 $self->insert(debug => sub { $logger->debug("script_runner: @_"); return 1;} );
54 $self->insert(alert => sub { $logger->warn("script_runner: @_"); return 1;} );
55 $self->insert(load_lib => sub { $self->load_lib(@_); });
57 # OpenSRF support functions
59 _OILS_FUNC_jsonopensrfrequest_send =>
60 sub { $self->_jsonopensrfrequest_send(@_); }
63 _OILS_FUNC_jsonopensrfrequest_connect =>
64 sub { $self->_jsonopensrfrequest_connect(@_); }
67 _OILS_FUNC_jsonopensrfrequest_disconnect =>
68 sub { $self->_jsonopensrfrequest_disconnect(@_); }
71 _OILS_FUNC_jsonopensrfrequest_finish =>
72 sub { $self->_jsonopensrfrequest_finish(@_); }
75 # XML support functions
77 _OILS_FUNC_xmlhttprequest_send =>
78 sub { $self->_xmlhttprequest_send(@_); }
81 _OILS_FUNC_xml_parse_string =>
82 sub { $self->_parse_xml_string(@_); }
85 $self->load_lib($_) for @{$self->{libs}};
92 $self->context->destroy;
93 $self->{_loaded} = {};
98 my( $self, $filename ) = @_;
99 $self->{file} = $filename;
102 sub runs { shift()->{_runs} }
108 $self->{reset_count} = $count if ($count);
109 return $self->{reset_count};
114 my $file = shift() || $self->{file};
115 my $js = $self->context;
119 $self->refresh_context
120 if ($self->reset_count && $self->runs > $self->reset_count);
124 $file = $self->_find_file($file);
125 $logger->debug("full script file path: $file");
127 if( ! open(F, $file) ) {
128 $logger->error("Error opening script file: $file");
135 if( !$js || !$content || !$js->eval($content) ) {
136 $logger->error("$file Eval failed: $@");
139 $logger->debug("eval of $file took ". sprintf('%0.3f', time - $s) . " seconds");
147 my( $self, $path ) = @_;
149 if ($self->{_path}{$path}) {
150 $self->{_path}{$path} = 0;
152 return $self->{_path}{$path};
154 if ($_paths{$path}) {
157 return $_paths{$path};
162 my( $self, $path ) = @_;
164 if (!$self->{_path}{$path}) {
165 $self->{_path}{$path} = 1;
168 if (!$_paths{$path}) {
178 for my $p ( keys %{ $self->{_path} } ) {
179 next unless ($self->{_path}{$p});
180 my $full = join('/',$p,$file);
181 return $full if (-e $full);
186 my( $self, $file ) = @_;
188 push @{ $self->{libs} }, $file
189 if (! grep {$_ eq $file} @{ $self->{libs} });
191 if (!$self->{_loaded}{$file} && $self->run( $file )) {
192 $self->{_loaded}{$file} = 1;
194 return $self->{_loaded}{$file};
204 my( $self, $key ) = @_;
205 return $self->context->property_get($key);
209 my( $self, $obj_key, $meth_name, $sub ) = @_;
210 my $obj = $self->context->object_by_path( $obj_key );
211 $self->context->function_set( $meth_name, $sub, $obj ) if $obj;
216 my( $self, $key, $val, $RO ) = @_;
217 return unless defined($key);
219 if (ref($val) =~ /^Fieldmapper/o) {
220 $self->insert_fm($key, $val, $RO);
221 } elsif (ref($val) and $val =~ /ARRAY/o) {
222 $self->insert_array($key, $val, $RO);
223 } elsif (ref($val) and $val =~ /HASH/o) {
224 $self->insert_hash($key, $val, $RO);
225 } elsif (ref($val) and $val =~ /CODE/o) {
226 $self->context->function_set( $key, $val );
227 } elsif (!ref($val)) {
228 if( defined($val) ) {
229 $self->context->property_by_path(
233 sub { my( $k, $v ) = @_; $val = $v; } :
238 $self->context->property_by_path($key, "");
250 my( $self, $key, $fm, $RO ) = @_;
251 my $ctx = $self->context;
252 return undef unless ($ctx and $key and $fm);
253 my $o = $ctx->object_by_path($key);
255 for my $f ( $fm->properties ) {
258 $self->insert("$key.$f", $val);
260 $ctx->property_by_path(
264 my $k = _js_prop_name(shift());
270 my $k = _js_prop_name(shift());
283 my( $self, $key, $hash, $RO ) = @_;
284 my $ctx = $self->context;
285 return undef unless ($ctx and $key and $hash);
286 $ctx->object_by_path($key);
288 for my $k ( keys %$hash ) {
291 $self->insert("$key.$k", $v);
293 $ctx->property_by_path(
295 sub { $hash->{_js_prop_name(shift())} },
298 my( $hashkey, $val ) = @_;
299 $hash->{_js_prop_name($hashkey)} = $val;
311 my( $self, $key, $array ) = @_;
312 my $ctx = $self->context;
313 return undef unless ($ctx and $key and $array);
315 my $a = $ctx->array_by_path($key);
318 for my $v ( @$array ) {
320 my $elobj = $ctx->object_by_path('__tmp_arr_el'.$__array_id);
321 $self->insert('__tmp_arr_el'.$__array_id, $v);
322 $ctx->array_set_element_as_object( $a, $ind, $elobj );
325 $ctx->array_set_element( $a, $ind, $v ) if defined($v);
331 sub _xmlhttprequest_send {
336 my $blocking = shift;
337 my $headerlist = shift;
340 my $ctx = $self->context;
342 # just so perl has access to it...
343 $ctx->object_by_path('__xmlhttpreq_hash.id'.$id);
345 my $headers = new HTTP::Headers;
346 my @lines = split(/\n/so, $headerlist);
347 for my $line (@lines) {
348 if ($line =~ /^(.+?)|(.+)$/o) {
349 $headers->header($1 => $2);
353 my $ua = LWP::UserAgent->new;
354 $ua->agent("OpenILS/0.1");
356 my $req = HTTP::Request->new($method => $url => $headers => $data);
357 my $res = $ua->request($req);
359 if ($res->is_success) {
361 $ctx->property_by_path('__xmlhttpreq_hash.id'.$id.'.responseText', $res->content);
362 $ctx->property_by_path('__xmlhttpreq_hash.id'.$id.'.readyState', 4);
363 $ctx->property_by_path('__xmlhttpreq_hash.id'.$id.'.statusText', $res->status_line);
364 $ctx->property_by_path('__xmlhttpreq_hash.id'.$id.'.status', $res->code);
370 our %_jsonopensrfrequest_cache = ();
372 sub _jsonopensrfrequest_connect {
377 my $ctx = $self->context;
378 $ctx->object_by_path('__jsonopensrfreq_hash.id'.$id);
380 my $ses = $_jsonopensrfrequest_cache{$id} ||
381 do { $_jsonopensrfrequest_cache{$id} = OpenSRF::AppSession->create($service) };
384 $ctx->property_by_path('__jsonopensrfreq_hash.id'.$id.'.connected', 1);
386 $ctx->property_by_path('__jsonopensrfreq_hash.id'.$id.'.connected', 0);
390 sub _jsonopensrfrequest_disconnect {
394 my $ctx = $self->context;
395 $ctx->object_by_path('__jsonopensrfreq_hash.id'.$id);
397 my $ses = $_jsonopensrfrequest_cache{$id};
403 sub _jsonopensrfrequest_finish {
407 my $ctx = $self->context;
408 $ctx->object_by_path('__jsonopensrfreq_hash.id'.$id);
410 my $ses = $_jsonopensrfrequest_cache{$id};
414 delete $_jsonopensrfrequest_cache{$id};
417 sub _jsonopensrfrequest_send {
422 my $blocking = shift;
425 my @p = @{ JSON->JSON2perl($params) };
427 my $ctx = $self->context;
429 # just so perl has access to it...
430 $ctx->object_by_path('__jsonopensrfreq_hash.id'.$id);
432 my $ses = $_jsonopensrfrequest_cache{$id} ||
433 do { $_jsonopensrfrequest_cache{$id} = OpenSRF::AppSession->create($service) };
434 my $req = $ses->request($method,@p);
438 my $res = $req->recv->content;
440 $ctx->property_by_path('__jsonopensrfreq_hash.id'.$id.'.responseText', JSON->perl2JSON($res));
441 $ctx->property_by_path('__jsonopensrfreq_hash.id'.$id.'.readyState', 4);
442 $ctx->property_by_path('__jsonopensrfreq_hash.id'.$id.'.statusText', 'OK');
443 $ctx->property_by_path('__jsonopensrfreq_hash.id'.$id.'.status', '200');
446 $ctx->property_by_path('__jsonopensrfreq_hash.id'.$id.'.responseText', '');
447 $ctx->property_by_path('__jsonopensrfreq_hash.id'.$id.'.readyState', 4);
448 $ctx->property_by_path('__jsonopensrfreq_hash.id'.$id.'.statusText', $req->failed->status );
449 $ctx->property_by_path('__jsonopensrfreq_hash.id'.$id.'.status', $req->failed->statusCode );
456 sub _parse_xml_string {
465 $doc = XML::LibXML->new->parse_string( $string );
469 warn "Could not parse document: $e\n";
473 _JS_DOM($self->context, $key, $doc);
481 if ($node->nodeType == 9) {
482 $node = $node->documentElement;
484 my $n = $node->nodeName;
485 my $ns = $node->namespaceURI;
486 $ns =~ s/'/\'/gso if ($ns);
487 $ns = "'$ns'" if ($ns);
488 $ns = 'null' unless ($ns);
491 #warn("$key = DOMImplementation().createDocument($ns,'$n');");
492 $ctx->eval("$key = new DOMImplementation().createDocument($ns,'$n');");
494 $key = $key.'.documentElement';
497 for my $a ($node->attributes) {
498 my $n = $a->nodeName;
502 #warn("$key.setAttribute('$n','$v');");
503 $ctx->eval("$key.setAttribute('$n','$v');");
508 for my $c ($node->childNodes) {
509 if ($c->nodeType == 1) {
510 my $n = $c->nodeName;
511 my $ns = $node->namespaceURI;
514 $ns =~ s/'/\'/gso if ($ns);
515 $ns = "'$ns'" if ($ns);
516 $ns = 'null' unless ($ns);
518 #warn("$key.appendChild($key.ownerDocument.createElementNS($ns,'$n'));");
519 $ctx->eval("$key.appendChild($key.ownerDocument.createElementNS($ns,'$n'));");
520 _JS_DOM($ctx, "$key.childNodes.item($k)",$c);
522 } elsif ($c->nodeType == 3) {
525 #warn("$key.appendChild($key.ownerDocument.createTextNode('$n'));");
526 #warn("path is $key.item($k);");
527 $ctx->eval("$key.appendChild($key.ownerDocument.createTextNode('$n'));");
529 } elsif ($c->nodeType == 4) {
532 #warn("$key.appendChild($key.ownerDocument.createCDATASection('$n'));");
533 $ctx->eval("$key.appendChild($key.ownerDocument.createCDATASection('$n'));");
535 } elsif ($c->nodeType == 8) {
538 #warn("$key.appendChild($key.ownerDocument.createComment('$n'));");
539 $ctx->eval("$key.appendChild($key.ownerDocument.createComment('$n'));");
542 warn "ACK! I don't know how to handle node type ".$c->nodeType;