1 package OpenILS::Application::Storage::Publisher;
2 use base qw/OpenILS::Application::Storage/;
5 use Digest::MD5 qw/md5_hex/;
6 use OpenSRF::EX qw/:try/;
8 use OpenSRF::Utils::Logger qw/:level/;
9 use OpenILS::Utils::Fieldmapper;
11 my $log = 'OpenSRF::Utils::Logger';
19 $class = ref($class) || $class;
21 $args{package} ||= $class;
22 __PACKAGE__->SUPER::register_method( %args );
24 if (exists($dup_args{cachable}) and $dup_args{cachable}) {
25 (my $name = $dup_args{api_name}) =~ s/^open-ils\.storage/open-ils.storage.cachable/o;
26 if ($name ne $dup_args{api_name}) {
27 $dup_args{real_api_name} = $dup_args{api_name};
28 $dup_args{method} = 'cachable_wrapper';
29 $dup_args{api_name} = $name;
30 $dup_args{package} = __PACKAGE__;
31 __PACKAGE__->SUPER::register_method( %dup_args );
35 if ($dup_args{real_api_name} =~ /^open-ils\.storage\.direct\..+\.search.+/o ||
36 $dup_args{api_name} =~ /^open-ils\.storage\.direct\..+\.search.+/o) {
37 $dup_args{api_name} = $dup_args{real_api_name} if ($dup_args{real_api_name});
39 (my $name = $dup_args{api_name}) =~ s/\.direct\./.id_list./o;
41 $dup_args{notes} = $dup_args{real_api_name};
42 $dup_args{real_api_name} = $dup_args{api_name};
43 $dup_args{method} = 'search_ids';
44 $dup_args{api_name} = $name;
45 $dup_args{package} = __PACKAGE__;
47 __PACKAGE__->SUPER::register_method( %dup_args );
51 sub cachable_wrapper {
60 cache_page_size => 1000,
64 my $key_string = $self->api_name;
65 for (my $ind = 0; $ind < scalar(@args); $ind++) {
66 if ( $args[$ind] eq 'limit' ||
67 $args[$ind] eq 'offset' ||
68 $args[$ind] eq 'cache_page_size' ||
69 $args[$ind] eq 'timeout' ) {
74 $cache_args{$args[$key_ind]} = $args[$value_ind];
75 $log->debug("Cache limiter value for $args[$key_ind] is $args[$value_ind]", INTERNAL);
78 $key_string .= $args[$ind];
79 $log->debug("Partial cache key value is $args[$ind]", INTERNAL);
80 push @real_args, $args[$ind];
83 my $cache_page = int($cache_args{offset} / $cache_args{cache_page_size});
86 $cache_key = md5_hex($key_string.$cache_page);
89 $log->debug("Key string for cache lookup is $key_string -> $cache_key", DEBUG);
90 $log->debug("Cache page is $cache_page", DEBUG);
92 my $cached_res = OpenSRF::Utils::Cache->new->get_cache( $cache_key );
93 if (defined $cached_res) {
94 $log->debug("Found ".scalar(@$cached_res)." records in the cache", INFO);
95 $log->debug("Values from cache: ".join(', ', @$cached_res), INTERNAL);
96 my $start = int($cache_args{offset} - ($cache_page * $cache_args{cache_page_size}));
97 my $end = int($start + $cache_args{limit} - 1);
98 $log->debug("Responding with values from ".$start.' to '.$end,DEBUG);
99 $client->respond( $_ ) for ( grep { defined } @$cached_res[ $start .. $end ]);
103 my $method = $self->method_lookup($self->{real_api_name});
104 my @res = $method->run(@real_args);
107 $client->respond( $_ ) for ( grep { defined } @res[$cache_args{offset} .. int($cache_args{offset} + $cache_args{limit} - 1)] );
109 $log->debug("Saving values from ".int($cache_page * $cache_args{cache_page_size})." to ".
110 int(($cache_page + 1) * $cache_args{cache_page_size}). "to the cache", INTERNAL);
112 OpenSRF::Utils::Cache->new->put_cache(
114 [@res[int($cache_page * $cache_args{cache_page_size}) .. int(($cache_page + 1) * $cache_args{cache_page_size}) ]] =>
115 OpenSRF::Utils->interval_to_seconds( $cache_args{timeout} )
119 $log->error("Cache seems to be down, $e");
130 my $cdbi = $self->{cdbi};
132 for my $id ( @ids ) {
135 my ($rec) = $cdbi->fast_fieldmapper($id);
136 if ($self->api_name !~ /batch/o) {
137 return $rec if ($rec);
139 $client->respond($rec);
149 my @res = $self->method_lookup($self->{real_api_name})->run(@args);
151 if (ref($res[0]) eq 'ARRAY') {
152 return [ map { $_->id } @{ $res[0] } ];
155 $client->respond($_) for ( map { $_->id } @res );
164 if (ref($args[0]) eq 'HASH') {
166 $args[1]{limit_dialect} = $self->{cdbi}->db_Main;
168 $args[1] = {limit_dialect => $self->{cdbi}->db_Main };
171 $args[0] = { @args };
172 $args[1] = {limit_dialect => $self->{cdbi} };
175 my $cdbi = $self->{cdbi};
177 for my $obj ($cdbi->search_where(@args)) {
178 next unless ref($obj);
179 $client->respond( $obj->to_fieldmapper );
189 my $cdbi = $self->{cdbi};
191 (my $search_type = $self->api_name) =~ s/.*\.(search[^.]*).*/$1/o;
193 for my $obj ($cdbi->$search_type(@args)) {
194 next unless ref($obj);
195 $client->respond( $obj->to_fieldmapper );
200 sub search_one_field {
205 (my $field = $self->api_name) =~ s/.*\.([^\.]+)$/$1/o;
207 return search( $self, $client, $field, @args );
210 sub old_search_one_field {
215 (my $search_type = $self->api_name) =~ s/.*\.(search[^.]*).*/$1/o;
216 (my $col = $self->api_name) =~ s/.*\.$search_type\.([^.]+).*/$1/;
217 my $cdbi = $self->{cdbi};
220 $like = 1 if ($search_type =~ /like$/o);
221 $like = 2 if ($search_type =~ /fts$/o);
222 $like = 3 if ($search_type =~ /regex$/o);
224 for my $term (@terms) {
225 $log->debug("Searching $cdbi for $col using type $search_type, value '$term'",DEBUG);
227 return [ $cdbi->fast_fieldmapper($term,$col,$like) ];
229 $client->respond( [ $cdbi->fast_fieldmapper($term,$col,$like) ] );
240 my $cdbi = $self->{cdbi};
244 my $rec = $cdbi->create($node);
245 $success = $rec->id if ($rec);
258 my $cdbi = $self->{cdbi};
260 return $cdbi->update($node);
268 my $where = 'WHERE ';
270 my $cdbi = $self->{cdbi};
271 my $table = $cdbi->table;
273 my @keys = sort keys %$search;
277 for my $col ( @keys ) {
278 if (ref($$search{$col}) and ref($$search{$col}) =~ /ARRAY/o) {
279 push @wheres, "$col IN (" . join(',', map { '?' } @{ $$search{$col} }) . ')';
280 push @binds, map { "$_" } @{ $$search{$col} };
282 push @wheres, "$col = ?";
283 push @binds, $$search{$col};
286 $where .= join ' AND ', @wheres;
288 my $delete = "DELETE FROM $table $where";
290 $log->debug("Performing MASS deletion : $delete",DEBUG);
292 my $dbh = $cdbi->db_Main;
295 my $sth = $dbh->prepare($delete);
296 $sth->execute( @binds );
298 $log->debug("MASS Delete succeeded",DEBUG);
300 $log->debug("MASS Delete FAILED : ".shift(),DEBUG);
306 sub remote_update_node {
312 my $cdbi = $self->{cdbi};
316 $success = $cdbi->remote_update($keys,$vals);
329 my $cdbi = $self->{cdbi};
333 $success = $cdbi->merge($keys,$vals)->id;
345 my $cdbi = $self->{cdbi};
349 $success = $cdbi->delete($node);
361 my $unwrap = $self->{unwrap};
363 my $cdbi = $self->{cdbi};
364 my $api_name = $self->api_name;
365 (my $single_call_api_name = $api_name) =~ s/batch\.//o;
367 $log->debug("Default $api_name looking up $single_call_api_name...",INTERNAL);
368 my $method = $self->method_lookup($single_call_api_name);
371 while ( my $node = shift(@nodes) ) {
372 my ($res) = $method->run( ($unwrap ? (@$node) : ($node)) );
373 push(@success, 1) if ($res >= 0);
376 my $insert_total = 0;
377 $insert_total += $_ for (@success);
379 return $insert_total;
382 OpenILS::Application::Storage::Publisher::actor->use;
384 $log->debug("ARG! Couldn't load actor class Publisher: $@", ERROR);
385 throw OpenSRF::EX::ERROR ("ARG! Couldn't load actor class Publisher: $@");
388 OpenILS::Application::Storage::Publisher::action->use;
390 $log->debug("ARG! Couldn't load action class Publisher: $@", ERROR);
391 throw OpenSRF::EX::ERROR ("ARG! Couldn't load action class Publisher: $@");
394 OpenILS::Application::Storage::Publisher::asset->use;
396 $log->debug("ARG! Couldn't load asset class Publisher: $@", ERROR);
397 throw OpenSRF::EX::ERROR ("ARG! Couldn't load asset class Publisher: $@");
400 OpenILS::Application::Storage::Publisher::biblio->use;
402 $log->debug("ARG! Couldn't load biblio class Publisher: $@", ERROR);
403 throw OpenSRF::EX::ERROR ("ARG! Couldn't load biblio class Publisher: $@");
406 OpenILS::Application::Storage::Publisher::config->use;
408 $log->debug("ARG! Couldn't load config class Publisher: $@", ERROR);
409 throw OpenSRF::EX::ERROR ("ARG! Couldn't load config class Publisher: $@");
412 OpenILS::Application::Storage::Publisher::metabib->use;
414 $log->debug("ARG! Couldn't load metabib class Publisher: $@", ERROR);
415 throw OpenSRF::EX::ERROR ("ARG! Couldn't load metabib class Publisher: $@");
418 OpenILS::Application::Storage::Publisher::authority->use;
420 $log->debug("ARG! Couldn't load authority class Publisher: $@", ERROR);
421 throw OpenSRF::EX::ERROR ("ARG! Couldn't load authority class Publisher: $@");
424 OpenILS::Application::Storage::Publisher::money->use;
426 $log->debug("ARG! Couldn't load money class Publisher: $@", ERROR);
427 throw OpenSRF::EX::ERROR ("ARG! Couldn't load money class Publisher: $@");
430 OpenILS::Application::Storage::Publisher::permission->use;
432 $log->debug("ARG! Couldn't load permission class Publisher: $@", ERROR);
433 throw OpenSRF::EX::ERROR ("ARG! Couldn't load permission class Publisher: $@");
436 OpenILS::Application::Storage::Publisher::container->use;
438 $log->debug("ARG! Couldn't load container class Publisher: $@", ERROR);
439 throw OpenSRF::EX::ERROR ("ARG! Couldn't load container class Publisher: $@");
444 for my $fmclass ( (Fieldmapper->classes) ) {
446 $log->debug("Generating methods for Fieldmapper class $fmclass", DEBUG);
448 next if ($fmclass->is_virtual);
450 (my $cdbi = $fmclass) =~ s/^Fieldmapper:://o;
451 (my $class = $cdbi) =~ s/::.*//o;
452 (my $api_class = $cdbi) =~ s/::/./go;
453 my $registration_class = __PACKAGE__ . "::$class";
454 my $api_prefix = 'open-ils.storage.direct.'.$api_class;
456 # Create the search methods
457 unless ( __PACKAGE__->is_registered( $api_prefix.'.search' ) ) {
458 __PACKAGE__->register_method(
459 api_name => $api_prefix.'.search',
469 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_where' ) ) {
470 __PACKAGE__->register_method(
471 api_name => $api_prefix.'.search_where',
472 method => 'search_where',
481 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_like' ) ) {
482 __PACKAGE__->register_method(
483 api_name => $api_prefix.'.search_like',
493 if (\&Class::DBI::search_fts) {
494 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_fts' ) ) {
495 __PACKAGE__->register_method(
496 api_name => $api_prefix.'.search_fts',
507 if (\&Class::DBI::search_regex) {
508 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_regex' ) ) {
509 __PACKAGE__->register_method(
510 api_name => $api_prefix.'.search_regex',
521 if (\&Class::DBI::search_ilike) {
522 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_ilike' ) ) {
523 __PACKAGE__->register_method(
524 api_name => $api_prefix.'.search_ilike',
535 # Create the retrieve method
536 unless ( __PACKAGE__->is_registered( $api_prefix.'.retrieve' ) ) {
537 __PACKAGE__->register_method(
538 api_name => $api_prefix.'.retrieve',
539 method => 'retrieve_node',
547 # Create the batch retrieve method
548 unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.retrieve' ) ) {
549 __PACKAGE__->register_method(
550 api_name => $api_prefix.'.batch.retrieve',
551 method => 'retrieve_node',
560 for my $field ($fmclass->real_fields) {
561 unless ( __PACKAGE__->is_registered( $api_prefix.'.search.'.$field ) ) {
562 __PACKAGE__->register_method(
563 api_name => $api_prefix.'.search.'.$field,
564 method => 'search_one_field',
572 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_like.'.$field ) ) {
573 __PACKAGE__->register_method(
574 api_name => $api_prefix.'.search_like.'.$field,
575 method => 'search_one_field',
583 if (\&Class::DBI::search_fts) {
584 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_fts.'.$field ) ) {
585 __PACKAGE__->register_method(
586 api_name => $api_prefix.'.search_fts.'.$field,
587 method => 'search_one_field',
596 if (\&Class::DBI::search_regex) {
597 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_regex.'.$field ) ) {
598 __PACKAGE__->register_method(
599 api_name => $api_prefix.'.search_regex.'.$field,
600 method => 'search_one_field',
609 if (\&Class::DBI::search_ilike) {
610 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_ilike.'.$field ) ) {
611 __PACKAGE__->register_method(
612 api_name => $api_prefix.'.search_ilike.'.$field,
613 method => 'search_one_field',
625 unless ($fmclass->is_readonly) {
626 # Create the create method
627 unless ( __PACKAGE__->is_registered( $api_prefix.'.create' ) ) {
628 __PACKAGE__->register_method(
629 api_name => $api_prefix.'.create',
630 method => 'create_node',
637 # Create the batch create method
638 unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.create' ) ) {
639 __PACKAGE__->register_method(
640 api_name => $api_prefix.'.batch.create',
641 method => 'batch_call',
648 # Create the update method
649 unless ( __PACKAGE__->is_registered( $api_prefix.'.update' ) ) {
650 __PACKAGE__->register_method(
651 api_name => $api_prefix.'.update',
652 method => 'update_node',
659 # Create the batch update method
660 unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.update' ) ) {
661 __PACKAGE__->register_method(
662 api_name => $api_prefix.'.batch.update',
663 method => 'batch_call',
670 # Create the delete method
671 unless ( __PACKAGE__->is_registered( $api_prefix.'.delete' ) ) {
672 __PACKAGE__->register_method(
673 api_name => $api_prefix.'.delete',
674 method => 'delete_node',
681 # Create the batch delete method
682 unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.delete' ) ) {
683 __PACKAGE__->register_method(
684 api_name => $api_prefix.'.batch.delete',
685 method => 'batch_call',
692 # Create the merge method
693 unless ( __PACKAGE__->is_registered( $api_prefix.'.merge' ) ) {
694 __PACKAGE__->register_method(
695 api_name => $api_prefix.'.merge',
696 method => 'merge_node',
703 # Create the batch merge method
704 unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.merge' ) ) {
705 __PACKAGE__->register_method(
706 api_name => $api_prefix.'.batch.merge',
707 method => 'batch_call',
715 # Create the remote_update method
716 unless ( __PACKAGE__->is_registered( $api_prefix.'.remote_update' ) ) {
717 __PACKAGE__->register_method(
718 api_name => $api_prefix.'.remote_update',
719 method => 'remote_update_node',
726 # Create the batch remote_update method
727 unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.remote_update' ) ) {
728 __PACKAGE__->register_method(
729 api_name => $api_prefix.'.batch.remote_update',
730 method => 'batch_call',
738 # Create the search-based mass delete method
739 unless ( __PACKAGE__->is_registered( $api_prefix.'.mass_delete' ) ) {
740 __PACKAGE__->register_method(
741 api_name => $api_prefix.'.mass_delete',
742 method => 'mass_delete',