1 package OpenILS::Utils::ScriptRunner;
2 use strict; use warnings;
3 use OpenSRF::Utils::Logger qw(:logger);
4 use OpenSRF::EX qw(:try);
5 use JavaScript::SpiderMonkey;
8 use Time::HiRes qw/time/;
15 $class = ref($class) || $class;
16 $params{paths} ||= [];
18 my $self = bless { file => $params{file},
19 libs => $params{libs},
20 _path => {%_paths} } => $class;
22 $self->add_path($_) for @{$params{paths}};
27 my( $self, $context ) = @_;
28 $self->{ctx} = $context if $context;
34 $self->context( new JavaScript::SpiderMonkey );
35 $self->context->init();
38 # eating our own dog food with insert
39 $self->insert(perl_print => sub { print "@_\n"; } );
40 $self->insert(perl_warn => sub { warn @_; } );
41 $self->insert(log_activity => sub { $logger->activity(@_); return 1;} );
42 $self->insert(log_error => sub { $logger->error(@_); return 1;} );
43 $self->insert(log_warn => sub { $logger->warn(@_); return 1;} );
44 $self->insert(log_info => sub { $logger->info(@_); return 1;} );
45 $self->insert(log_debug => sub { $logger->debug(@_); return 1;} );
46 $self->insert(log_internal => sub { $logger->internal(@_); return 1;} );
47 $self->insert(debug => sub { $logger->debug(@_); return 1;} );
48 $self->insert(alert => sub { $logger->warn(@_); return 1;} );
49 $self->insert(load_lib => sub { $self->load_lib(@_); });
51 # XML support functions
53 _OILS_FUNC_xmlhttprequest_send => sub { $self->_xmlhttprequest_send(@_); });
55 _OILS_FUNC_xml_parse_string => sub { $self->_parse_xml_string(@_); });
57 $self->load_lib($_) for @{$self->{libs}};
64 $self->context->destroy;
65 $self->{_loaded} = {};
70 my( $self, $filename ) = @_;
71 $self->{file} = $filename;
76 my $file = shift() || $self->{file};
77 my $js = $self->context;
79 $file = $self->_find_file($file);
81 if( ! open(F, $file) ) {
82 $logger->error("Error opening script file: $file");
89 if( !$js || !$content || !$js->eval($content) ) {
90 $logger->error("$file Eval failed: $@");
93 $logger->debug("eval of $file took ". sprintf('%0.3f', time - $s) . " seconds");
101 my( $self, $path ) = @_;
103 if ($self->{_path}{$path}) {
104 $self->{_path}{$path} = 0;
106 return $self->{_path}{$path};
108 if ($_paths{$path}) {
111 return $_paths{$path};
116 my( $self, $path ) = @_;
118 if (!$self->{_path}{$path}) {
119 $self->{_path}{$path} = 1;
122 if (!$_paths{$path}) {
132 for my $p ( keys %{ $self->{_path} } ) {
133 next unless ($self->{_path}{$p});
134 my $full = join('/',$p,$file);
135 return $full if (-e $full);
140 my( $self, $file ) = @_;
141 if (!$self->{_loaded}{$file} && $self->run( $file )) {
142 $self->{_loaded}{$file} = 1;
144 return $self->{_loaded}{$file};
154 my( $self, $key ) = @_;
155 return $self->context->property_get($key);
159 my( $self, $obj_key, $meth_name, $sub ) = @_;
160 my $obj = $self->context->object_by_path( $obj_key );
161 $self->context->function_set( $meth_name, $sub, $obj ) if $obj;
166 my( $self, $key, $val, $RO ) = @_;
167 return unless defined($key);
169 if (ref($val) =~ /^Fieldmapper/o) {
170 $self->insert_fm($key, $val, $RO);
171 } elsif (ref($val) and $val =~ /ARRAY/o) {
172 $self->insert_array($key, $val, $RO);
173 } elsif (ref($val) and $val =~ /HASH/o) {
174 $self->insert_hash($key, $val, $RO);
175 } elsif (ref($val) and $val =~ /CODE/o) {
176 $self->context->function_set( $key, $val );
177 } elsif (!ref($val)) {
178 if( defined($val) ) {
179 $self->context->property_by_path(
183 sub { my( $k, $v ) = @_; $val = $v; } :
188 $self->context->property_by_path($key);
200 my( $self, $key, $fm, $RO ) = @_;
201 my $ctx = $self->context;
202 return undef unless ($ctx and $key and $fm);
203 my $o = $ctx->object_by_path($key);
205 for my $f ( $fm->properties ) {
208 $self->insert("$key.$f", $val);
210 $ctx->property_by_path(
214 my $k = _js_prop_name(shift());
220 my $k = _js_prop_name(shift());
233 my( $self, $key, $hash, $RO ) = @_;
234 my $ctx = $self->context;
235 return undef unless ($ctx and $key and $hash);
236 $ctx->object_by_path($key);
238 for my $k ( keys %$hash ) {
241 $self->insert("$key.$k", $v);
243 $ctx->property_by_path(
245 sub { $hash->{_js_prop_name(shift())} },
248 my( $hashkey, $val ) = @_;
249 $hash->{_js_prop_name($hashkey)} = $val;
261 my( $self, $key, $array ) = @_;
262 my $ctx = $self->context;
263 return undef unless ($ctx and $key and $array);
265 my $a = $ctx->array_by_path($key);
268 for my $v ( @$array ) {
270 my $elobj = $ctx->object_by_path('__tmp_arr_el'.$__array_id);
271 $self->insert('__tmp_arr_el'.$__array_id, $v);
272 $ctx->array_set_element_as_object( $a, $ind, $elobj );
275 $ctx->array_set_element( $a, $ind, $v ) if defined($v);
281 sub _xmlhttprequest_send {
286 my $blocking = shift;
287 my $headerlist = shift;
290 my $ctx = $self->context;
292 # just so perl has access to it...
293 $ctx->object_by_path('__xmlhttpreq_hash.id'.$id);
295 my $headers = new HTTP::Headers;
296 my @lines = split(/\n/so, $headerlist);
297 for my $line (@lines) {
298 if ($line =~ /^(.+?)|(.+)$/o) {
299 $headers->header($1 => $2);
303 my $ua = LWP::UserAgent->new;
304 $ua->agent("OpenILS/0.1");
306 my $req = HTTP::Request->new($method => $url => $headers => $data);
307 my $res = $ua->request($req);
309 if ($res->is_success) {
311 $ctx->property_by_path('__xmlhttpreq_hash.id'.$id.'.responseText', $res->content);
312 $ctx->property_by_path('__xmlhttpreq_hash.id'.$id.'.readyState', 4);
313 $ctx->property_by_path('__xmlhttpreq_hash.id'.$id.'.statusText', $res->status_line);
314 $ctx->property_by_path('__xmlhttpreq_hash.id'.$id.'.status', $res->code);
320 sub _parse_xml_string {
329 $doc = XML::LibXML->new->parse_string( $string );
333 warn "Could not parse document: $e\n";
337 _JS_DOM($self->context, $key, $doc);
345 if ($node->nodeType == 9) {
346 $node = $node->documentElement;
348 my $n = $node->nodeName;
349 my $ns = $node->namespaceURI;
350 $ns =~ s/'/\'/gso if ($ns);
351 $ns = "'$ns'" if ($ns);
352 $ns = 'null' unless ($ns);
355 #warn("$key = DOMImplementation().createDocument($ns,'$n');");
356 $ctx->eval("$key = new DOMImplementation().createDocument($ns,'$n');");
358 $key = $key.'.documentElement';
361 for my $a ($node->attributes) {
362 my $n = $a->nodeName;
366 #warn("$key.setAttribute('$n','$v');");
367 $ctx->eval("$key.setAttribute('$n','$v');");
372 for my $c ($node->childNodes) {
373 if ($c->nodeType == 1) {
374 my $n = $c->nodeName;
375 my $ns = $node->namespaceURI;
378 $ns =~ s/'/\'/gso if ($ns);
379 $ns = "'$ns'" if ($ns);
380 $ns = 'null' unless ($ns);
382 #warn("$key.appendChild($key.ownerDocument.createElementNS($ns,'$n'));");
383 $ctx->eval("$key.appendChild($key.ownerDocument.createElementNS($ns,'$n'));");
384 _JS_DOM($ctx, "$key.childNodes.item($k)",$c);
386 } elsif ($c->nodeType == 3) {
389 #warn("$key.appendChild($key.ownerDocument.createTextNode('$n'));");
390 #warn("path is $key.item($k);");
391 $ctx->eval("$key.appendChild($key.ownerDocument.createTextNode('$n'));");
393 } elsif ($c->nodeType == 4) {
396 #warn("$key.appendChild($key.ownerDocument.createCDATASection('$n'));");
397 $ctx->eval("$key.appendChild($key.ownerDocument.createCDATASection('$n'));");
399 } elsif ($c->nodeType == 8) {
402 #warn("$key.appendChild($key.ownerDocument.createComment('$n'));");
403 $ctx->eval("$key.appendChild($key.ownerDocument.createComment('$n'));");
406 warn "ACK! I don't know how to handle node type ".$c->nodeType;