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} ||= [];
19 my $self = bless { file => $params{file},
20 libs => $params{libs},
21 _path => {%_paths} } => $class;
23 $self->add_path($_) for @{$params{paths}};
28 my( $self, $context ) = @_;
29 $self->{ctx} = $context if $context;
35 $self->context( new JavaScript::SpiderMonkey );
36 $self->context->init();
39 # eating our own dog food with insert
40 $self->insert(perl_print => sub { print "@_\n"; } );
41 $self->insert(perl_warn => sub { warn @_; } );
42 $self->insert(log_activity => sub { $logger->activity(@_); return 1;} );
43 $self->insert(log_error => sub { $logger->error(@_); return 1;} );
44 $self->insert(log_warn => sub { $logger->warn(@_); return 1;} );
45 $self->insert(log_info => sub { $logger->info(@_); return 1;} );
46 $self->insert(log_debug => sub { $logger->debug(@_); return 1;} );
47 $self->insert(log_internal => sub { $logger->internal(@_); return 1;} );
48 $self->insert(debug => sub { $logger->debug(@_); return 1;} );
49 $self->insert(alert => sub { $logger->warn(@_); return 1;} );
50 $self->insert(load_lib => sub { $self->load_lib(@_); });
52 # OpenSRF support function
54 _OILS_FUNC_jsonopensrfrequest_send =>
55 sub { $self->_jsonopensrfrequest_send(@_); }
58 # XML support functions
60 _OILS_FUNC_xmlhttprequest_send =>
61 sub { $self->_xmlhttprequest_send(@_); }
64 _OILS_FUNC_xml_parse_string =>
65 sub { $self->_parse_xml_string(@_); }
68 $self->load_lib($_) for @{$self->{libs}};
75 $self->context->destroy;
76 $self->{_loaded} = {};
81 my( $self, $filename ) = @_;
82 $self->{file} = $filename;
87 my $file = shift() || $self->{file};
88 my $js = $self->context;
90 $file = $self->_find_file($file);
92 if( ! open(F, $file) ) {
93 $logger->error("Error opening script file: $file");
100 if( !$js || !$content || !$js->eval($content) ) {
101 $logger->error("$file Eval failed: $@");
104 $logger->debug("eval of $file took ". sprintf('%0.3f', time - $s) . " seconds");
112 my( $self, $path ) = @_;
114 if ($self->{_path}{$path}) {
115 $self->{_path}{$path} = 0;
117 return $self->{_path}{$path};
119 if ($_paths{$path}) {
122 return $_paths{$path};
127 my( $self, $path ) = @_;
129 if (!$self->{_path}{$path}) {
130 $self->{_path}{$path} = 1;
133 if (!$_paths{$path}) {
143 for my $p ( keys %{ $self->{_path} } ) {
144 next unless ($self->{_path}{$p});
145 my $full = join('/',$p,$file);
146 return $full if (-e $full);
151 my( $self, $file ) = @_;
152 if (!$self->{_loaded}{$file} && $self->run( $file )) {
153 $self->{_loaded}{$file} = 1;
155 return $self->{_loaded}{$file};
165 my( $self, $key ) = @_;
166 return $self->context->property_get($key);
170 my( $self, $obj_key, $meth_name, $sub ) = @_;
171 my $obj = $self->context->object_by_path( $obj_key );
172 $self->context->function_set( $meth_name, $sub, $obj ) if $obj;
177 my( $self, $key, $val, $RO ) = @_;
178 return unless defined($key);
180 if (ref($val) =~ /^Fieldmapper/o) {
181 $self->insert_fm($key, $val, $RO);
182 } elsif (ref($val) and $val =~ /ARRAY/o) {
183 $self->insert_array($key, $val, $RO);
184 } elsif (ref($val) and $val =~ /HASH/o) {
185 $self->insert_hash($key, $val, $RO);
186 } elsif (ref($val) and $val =~ /CODE/o) {
187 $self->context->function_set( $key, $val );
188 } elsif (!ref($val)) {
189 if( defined($val) ) {
190 $self->context->property_by_path(
194 sub { my( $k, $v ) = @_; $val = $v; } :
199 $self->context->property_by_path($key, "");
211 my( $self, $key, $fm, $RO ) = @_;
212 my $ctx = $self->context;
213 return undef unless ($ctx and $key and $fm);
214 my $o = $ctx->object_by_path($key);
216 for my $f ( $fm->properties ) {
219 $self->insert("$key.$f", $val);
221 $ctx->property_by_path(
225 my $k = _js_prop_name(shift());
231 my $k = _js_prop_name(shift());
244 my( $self, $key, $hash, $RO ) = @_;
245 my $ctx = $self->context;
246 return undef unless ($ctx and $key and $hash);
247 $ctx->object_by_path($key);
249 for my $k ( keys %$hash ) {
252 $self->insert("$key.$k", $v);
254 $ctx->property_by_path(
256 sub { $hash->{_js_prop_name(shift())} },
259 my( $hashkey, $val ) = @_;
260 $hash->{_js_prop_name($hashkey)} = $val;
272 my( $self, $key, $array ) = @_;
273 my $ctx = $self->context;
274 return undef unless ($ctx and $key and $array);
276 my $a = $ctx->array_by_path($key);
279 for my $v ( @$array ) {
281 my $elobj = $ctx->object_by_path('__tmp_arr_el'.$__array_id);
282 $self->insert('__tmp_arr_el'.$__array_id, $v);
283 $ctx->array_set_element_as_object( $a, $ind, $elobj );
286 $ctx->array_set_element( $a, $ind, $v ) if defined($v);
292 sub _xmlhttprequest_send {
297 my $blocking = shift;
298 my $headerlist = shift;
301 my $ctx = $self->context;
303 # just so perl has access to it...
304 $ctx->object_by_path('__xmlhttpreq_hash.id'.$id);
306 my $headers = new HTTP::Headers;
307 my @lines = split(/\n/so, $headerlist);
308 for my $line (@lines) {
309 if ($line =~ /^(.+?)|(.+)$/o) {
310 $headers->header($1 => $2);
314 my $ua = LWP::UserAgent->new;
315 $ua->agent("OpenILS/0.1");
317 my $req = HTTP::Request->new($method => $url => $headers => $data);
318 my $res = $ua->request($req);
320 if ($res->is_success) {
322 $ctx->property_by_path('__xmlhttpreq_hash.id'.$id.'.responseText', $res->content);
323 $ctx->property_by_path('__xmlhttpreq_hash.id'.$id.'.readyState', 4);
324 $ctx->property_by_path('__xmlhttpreq_hash.id'.$id.'.statusText', $res->status_line);
325 $ctx->property_by_path('__xmlhttpreq_hash.id'.$id.'.status', $res->code);
331 sub _jsonopensrfrequest_send {
336 my $blocking = shift;
339 my @p = @{ JSON->JSON2perl($params) };
341 my $ctx = $self->context;
343 # just so perl has access to it...
344 $ctx->object_by_path('__jsonopensrfreq_hash.id'.$id);
346 my $ses = OpenSRF::AppSession->create($service);
347 my $req = $ses->request($method,@p);
351 my $res = $req->content;
353 $ctx->property_by_path('__jsonopensrfreq_hash.id'.$id.'.responseText', $res);
354 $ctx->property_by_path('__jsonopensrfreq_hash.id'.$id.'.readyState', 4);
355 $ctx->property_by_path('__jsonopensrfreq_hash.id'.$id.'.statusText', 'OK');
356 $ctx->property_by_path('__jsonopensrfreq_hash.id'.$id.'.status', '200');
359 $ctx->property_by_path('__jsonopensrfreq_hash.id'.$id.'.responseText', '');
360 $ctx->property_by_path('__jsonopensrfreq_hash.id'.$id.'.readyState', 4);
361 $ctx->property_by_path('__jsonopensrfreq_hash.id'.$id.'.statusText', $req->failed->status );
362 $ctx->property_by_path('__jsonopensrfreq_hash.id'.$id.'.status', $req->failed->statusCode );
369 sub _parse_xml_string {
378 $doc = XML::LibXML->new->parse_string( $string );
382 warn "Could not parse document: $e\n";
386 _JS_DOM($self->context, $key, $doc);
394 if ($node->nodeType == 9) {
395 $node = $node->documentElement;
397 my $n = $node->nodeName;
398 my $ns = $node->namespaceURI;
399 $ns =~ s/'/\'/gso if ($ns);
400 $ns = "'$ns'" if ($ns);
401 $ns = 'null' unless ($ns);
404 #warn("$key = DOMImplementation().createDocument($ns,'$n');");
405 $ctx->eval("$key = new DOMImplementation().createDocument($ns,'$n');");
407 $key = $key.'.documentElement';
410 for my $a ($node->attributes) {
411 my $n = $a->nodeName;
415 #warn("$key.setAttribute('$n','$v');");
416 $ctx->eval("$key.setAttribute('$n','$v');");
421 for my $c ($node->childNodes) {
422 if ($c->nodeType == 1) {
423 my $n = $c->nodeName;
424 my $ns = $node->namespaceURI;
427 $ns =~ s/'/\'/gso if ($ns);
428 $ns = "'$ns'" if ($ns);
429 $ns = 'null' unless ($ns);
431 #warn("$key.appendChild($key.ownerDocument.createElementNS($ns,'$n'));");
432 $ctx->eval("$key.appendChild($key.ownerDocument.createElementNS($ns,'$n'));");
433 _JS_DOM($ctx, "$key.childNodes.item($k)",$c);
435 } elsif ($c->nodeType == 3) {
438 #warn("$key.appendChild($key.ownerDocument.createTextNode('$n'));");
439 #warn("path is $key.item($k);");
440 $ctx->eval("$key.appendChild($key.ownerDocument.createTextNode('$n'));");
442 } elsif ($c->nodeType == 4) {
445 #warn("$key.appendChild($key.ownerDocument.createCDATASection('$n'));");
446 $ctx->eval("$key.appendChild($key.ownerDocument.createCDATASection('$n'));");
448 } elsif ($c->nodeType == 8) {
451 #warn("$key.appendChild($key.ownerDocument.createComment('$n'));");
452 $ctx->eval("$key.appendChild($key.ownerDocument.createComment('$n'));");
455 warn "ACK! I don't know how to handle node type ".$c->nodeType;