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 );
36 sub cachable_wrapper {
45 cache_page_size => 1000,
49 my $key_string = $self->api_name;
50 for (my $ind = 0; $ind < scalar(@args); $ind++) {
51 if ( $args[$ind] eq 'limit' ||
52 $args[$ind] eq 'offset' ||
53 $args[$ind] eq 'cache_page_size' ||
54 $args[$ind] eq 'timeout' ) {
59 $cache_args{$args[$key_ind]} = $args[$value_ind];
60 $log->debug("Cache limiter value for $args[$key_ind] is $args[$value_ind]", INTERNAL);
63 $key_string .= $args[$ind];
64 $log->debug("Partial cache key value is $args[$ind]", INTERNAL);
65 push @real_args, $args[$ind];
68 my $cache_page = int($cache_args{offset} / $cache_args{cache_page_size});
71 $cache_key = md5_hex($key_string.$cache_page);
74 $log->debug("Key string for cache lookup is $key_string -> $cache_key", DEBUG);
75 $log->debug("Cache page is $cache_page", DEBUG);
77 my $cached_res = OpenSRF::Utils::Cache->new->get_cache( $cache_key );
78 if (defined $cached_res) {
79 $log->debug("Found ".scalar(@$cached_res)." records in the cache", INFO);
80 $log->debug("Values from cache: ".join(', ', @$cached_res), INTERNAL);
81 my $start = int($cache_args{offset} - ($cache_page * $cache_args{cache_page_size}));
82 my $end = int($start + $cache_args{limit} - 1);
83 $log->debug("Responding with values from ".$start.' to '.$end,DEBUG);
84 $client->respond( $_ ) for ( grep { defined } @$cached_res[ $start .. $end ]);
88 my $method = $self->method_lookup($self->{real_api_name});
89 my @res = $method->run(@real_args);
92 $client->respond( $_ ) for ( grep { defined } @res[$cache_args{offset} .. int($cache_args{offset} + $cache_args{limit} - 1)] );
94 $log->debug("Saving values from ".int($cache_page * $cache_args{cache_page_size})." to ".
95 int(($cache_page + 1) * $cache_args{cache_page_size}). "to the cache", INTERNAL);
97 OpenSRF::Utils::Cache->new->put_cache(
99 [@res[int($cache_page * $cache_args{cache_page_size}) .. int(($cache_page + 1) * $cache_args{cache_page_size}) ]] =>
100 OpenSRF::Utils->interval_to_seconds( $cache_args{timeout} )
104 $log->error("Cache seems to be down, $e");
115 my $cdbi = $self->{cdbi};
117 for my $id ( @ids ) {
120 my ($rec) = $cdbi->fast_fieldmapper($id);
121 if ($self->api_name !~ /batch/o) {
122 return $rec if ($rec);
124 $client->respond($rec);
134 my $cdbi = $self->{cdbi};
136 for my $obj ($cdbi->search_where(@args)) {
137 next unless ref($obj);
138 $client->respond( $obj->to_fieldmapper );
148 my $cdbi = $self->{cdbi};
150 (my $search_type = $self->api_name) =~ s/.*\.(search[^.]*).*/$1/o;
152 for my $obj ($cdbi->$search_type(@args)) {
153 next unless ref($obj);
154 $client->respond( $obj->to_fieldmapper );
159 sub search_one_field {
164 (my $field = $self->api_name) =~ s/.*\.([^\.]+)$/$1/o;
166 return search( $self, $client, $field, @args );
169 sub old_search_one_field {
174 (my $search_type = $self->api_name) =~ s/.*\.(search[^.]*).*/$1/o;
175 (my $col = $self->api_name) =~ s/.*\.$search_type\.([^.]+).*/$1/;
176 my $cdbi = $self->{cdbi};
179 $like = 1 if ($search_type =~ /like$/o);
180 $like = 2 if ($search_type =~ /fts$/o);
181 $like = 3 if ($search_type =~ /regex$/o);
183 for my $term (@terms) {
184 $log->debug("Searching $cdbi for $col using type $search_type, value '$term'",DEBUG);
186 return [ $cdbi->fast_fieldmapper($term,$col,$like) ];
188 $client->respond( [ $cdbi->fast_fieldmapper($term,$col,$like) ] );
199 my $cdbi = $self->{cdbi};
203 my $rec = $cdbi->create($node);
204 $success = $rec->id if ($rec);
217 my $cdbi = $self->{cdbi};
219 return $cdbi->update($node);
227 my $where = 'WHERE ';
229 my $cdbi = $self->{cdbi};
230 my $table = $cdbi->table;
232 my @keys = sort keys %$search;
236 for my $col ( @keys ) {
237 if (ref($$search{$col}) and ref($$search{$col}) =~ /ARRAY/o) {
238 push @wheres, "$col IN (" . join(',', map { '?' } @{ $$search{$col} }) . ')';
239 push @binds, map { "$_" } @{ $$search{$col} };
241 push @wheres, "$col = ?";
242 push @binds, $$search{$col};
245 $where .= join ' AND ', @wheres;
247 my $delete = "DELETE FROM $table $where";
249 $log->debug("Performing MASS deletion : $delete",DEBUG);
251 my $dbh = $cdbi->db_Main;
254 my $sth = $dbh->prepare($delete);
255 $sth->execute( @binds );
257 $log->debug("MASS Delete succeeded",DEBUG);
259 $log->debug("MASS Delete FAILED : ".shift(),DEBUG);
265 sub remote_update_node {
271 my $cdbi = $self->{cdbi};
275 $success = $cdbi->remote_update($keys,$vals);
288 my $cdbi = $self->{cdbi};
292 $success = $cdbi->merge($keys,$vals)->id;
304 my $cdbi = $self->{cdbi};
308 $success = $cdbi->delete($node);
320 my $unwrap = $self->{unwrap};
322 my $cdbi = $self->{cdbi};
323 my $api_name = $self->api_name;
324 (my $single_call_api_name = $api_name) =~ s/batch\.//o;
326 $log->debug("Default $api_name looking up $single_call_api_name...",INTERNAL);
327 my $method = $self->method_lookup($single_call_api_name);
330 while ( my $node = shift(@nodes) ) {
331 my ($res) = $method->run( ($unwrap ? (@$node) : ($node)) );
332 push(@success, 1) if ($res >= 0);
335 my $insert_total = 0;
336 $insert_total += $_ for (@success);
338 return $insert_total;
342 use OpenILS::Application::Storage::Publisher::actor;
343 use OpenILS::Application::Storage::Publisher::action;
344 use OpenILS::Application::Storage::Publisher::asset;
345 use OpenILS::Application::Storage::Publisher::biblio;
346 use OpenILS::Application::Storage::Publisher::config;
347 use OpenILS::Application::Storage::Publisher::metabib;
348 use OpenILS::Application::Storage::Publisher::authority;
349 use OpenILS::Application::Storage::Publisher::money;
350 use OpenILS::Application::Storage::Publisher::permission;
351 use OpenILS::Application::Storage::Publisher::container;
355 $log->debug("ARG! Couldn't load (at least one) class Publisher: $@", ERROR);
356 throw OpenSRF::EX::ERROR ("ARG! Couldn't load (at least one) class Publisher: $@");
360 for my $fmclass ( (Fieldmapper->classes) ) {
362 $log->debug("Generating methods for Fieldmapper class $fmclass", DEBUG);
364 next if ($fmclass->is_virtual);
366 (my $cdbi = $fmclass) =~ s/^Fieldmapper:://o;
367 (my $class = $cdbi) =~ s/::.*//o;
368 (my $api_class = $cdbi) =~ s/::/./go;
369 my $registration_class = __PACKAGE__ . "::$class";
370 my $api_prefix = 'open-ils.storage.direct.'.$api_class;
372 # Create the search methods
373 unless ( __PACKAGE__->is_registered( $api_prefix.'.search' ) ) {
374 __PACKAGE__->register_method(
375 api_name => $api_prefix.'.search',
385 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_where' ) ) {
386 __PACKAGE__->register_method(
387 api_name => $api_prefix.'.search_where',
388 method => 'search_where',
397 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_like' ) ) {
398 __PACKAGE__->register_method(
399 api_name => $api_prefix.'.search_like',
409 if (\&Class::DBI::search_fts) {
410 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_fts' ) ) {
411 __PACKAGE__->register_method(
412 api_name => $api_prefix.'.search_fts',
423 if (\&Class::DBI::search_regex) {
424 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_regex' ) ) {
425 __PACKAGE__->register_method(
426 api_name => $api_prefix.'.search_regex',
437 if (\&Class::DBI::search_ilike) {
438 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_ilike' ) ) {
439 __PACKAGE__->register_method(
440 api_name => $api_prefix.'.search_ilike',
451 # Create the retrieve method
452 unless ( __PACKAGE__->is_registered( $api_prefix.'.retrieve' ) ) {
453 __PACKAGE__->register_method(
454 api_name => $api_prefix.'.retrieve',
455 method => 'retrieve_node',
463 # Create the batch retrieve method
464 unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.retrieve' ) ) {
465 __PACKAGE__->register_method(
466 api_name => $api_prefix.'.batch.retrieve',
467 method => 'retrieve_node',
476 for my $field ($fmclass->real_fields) {
477 unless ( __PACKAGE__->is_registered( $api_prefix.'.search.'.$field ) ) {
478 __PACKAGE__->register_method(
479 api_name => $api_prefix.'.search.'.$field,
480 method => 'search_one_field',
488 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_like.'.$field ) ) {
489 __PACKAGE__->register_method(
490 api_name => $api_prefix.'.search_like.'.$field,
491 method => 'search_one_field',
499 if (\&Class::DBI::search_fts) {
500 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_fts.'.$field ) ) {
501 __PACKAGE__->register_method(
502 api_name => $api_prefix.'.search_fts.'.$field,
503 method => 'search_one_field',
512 if (\&Class::DBI::search_regex) {
513 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_regex.'.$field ) ) {
514 __PACKAGE__->register_method(
515 api_name => $api_prefix.'.search_regex.'.$field,
516 method => 'search_one_field',
525 if (\&Class::DBI::search_ilike) {
526 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_ilike.'.$field ) ) {
527 __PACKAGE__->register_method(
528 api_name => $api_prefix.'.search_ilike.'.$field,
529 method => 'search_one_field',
541 unless ($fmclass->is_readonly) {
542 # Create the create method
543 unless ( __PACKAGE__->is_registered( $api_prefix.'.create' ) ) {
544 __PACKAGE__->register_method(
545 api_name => $api_prefix.'.create',
546 method => 'create_node',
553 # Create the batch create method
554 unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.create' ) ) {
555 __PACKAGE__->register_method(
556 api_name => $api_prefix.'.batch.create',
557 method => 'batch_call',
564 # Create the update method
565 unless ( __PACKAGE__->is_registered( $api_prefix.'.update' ) ) {
566 __PACKAGE__->register_method(
567 api_name => $api_prefix.'.update',
568 method => 'update_node',
575 # Create the batch update method
576 unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.update' ) ) {
577 __PACKAGE__->register_method(
578 api_name => $api_prefix.'.batch.update',
579 method => 'batch_call',
586 # Create the delete method
587 unless ( __PACKAGE__->is_registered( $api_prefix.'.delete' ) ) {
588 __PACKAGE__->register_method(
589 api_name => $api_prefix.'.delete',
590 method => 'delete_node',
597 # Create the batch delete method
598 unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.delete' ) ) {
599 __PACKAGE__->register_method(
600 api_name => $api_prefix.'.batch.delete',
601 method => 'batch_call',
608 # Create the merge method
609 unless ( __PACKAGE__->is_registered( $api_prefix.'.merge' ) ) {
610 __PACKAGE__->register_method(
611 api_name => $api_prefix.'.merge',
612 method => 'merge_node',
619 # Create the batch merge method
620 unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.merge' ) ) {
621 __PACKAGE__->register_method(
622 api_name => $api_prefix.'.batch.merge',
623 method => 'batch_call',
631 # Create the remote_update method
632 unless ( __PACKAGE__->is_registered( $api_prefix.'.remote_update' ) ) {
633 __PACKAGE__->register_method(
634 api_name => $api_prefix.'.remote_update',
635 method => 'remote_update_node',
642 # Create the batch remote_update method
643 unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.remote_update' ) ) {
644 __PACKAGE__->register_method(
645 api_name => $api_prefix.'.batch.remote_update',
646 method => 'batch_call',
654 # Create the search-based mass delete method
655 unless ( __PACKAGE__->is_registered( $api_prefix.'.mass_delete' ) ) {
656 __PACKAGE__->register_method(
657 api_name => $api_prefix.'.mass_delete',
658 method => 'mass_delete',