]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher.pm
fleshed out "actor.usr" interface, and stream behavior adjustment
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Storage / Publisher.pm
1 package OpenILS::Application::Storage::Publisher;
2 use base qw/OpenILS::Application::Storage/;
3 our $VERSION = 1;
4
5 use Digest::MD5 qw/md5_hex/;
6 use OpenSRF::EX qw/:try/;;
7 use OpenSRF::Utils::Logger;
8 my $log = 'OpenSRF::Utils::Logger';
9
10 use OpenILS::Utils::Fieldmapper;
11 #use OpenILS::Application::Storage::CDBI;
12
13 #use OpenILS::Application::Storage::CDBI::actor;
14 #use OpenILS::Application::Storage::CDBI::asset;
15 #use OpenILS::Application::Storage::CDBI::biblio;
16 #use OpenILS::Application::Storage::CDBI::config;
17 #use OpenILS::Application::Storage::CDBI::metabib;
18
19 sub register_method {
20         my $class = shift;
21         my %args = @_;
22         my %dup_args = %args;
23
24         $class = ref($class) || $class;
25
26         $args{package} ||= $class;
27         __PACKAGE__->SUPER::register_method( %args );
28
29         if (exists($dup_args{cachable}) and $dup_args{cachable}) {
30                 (my $name = $dup_args{api_name}) =~ s/^open-ils\.storage/open-ils.storage.cachable/o;
31                 if ($name ne $dup_args{api_name}) {
32                         $dup_args{real_api_name} = $dup_args{api_name};
33                         $dup_args{method} = 'cachable_wrapper';
34                         $dup_args{api_name} = $name;
35                         $dup_args{package} = __PACKAGE__;
36                         __PACKAGE__->SUPER::register_method( %dup_args );
37                 }
38         }
39 }
40
41 sub cachable_wrapper {
42         my $self = shift;
43         my $client = shift;
44         my @args = @_;
45
46         my %cache_args = (
47                 limit   => 100,
48                 offset  => 0,
49                 timeout => 300,
50         );
51
52         my @real_args;
53         my $key_string = $self->api_name;
54         for (my $ind = 0; $ind < scalar(@args); $ind++) {
55                 if (    "$args[$ind]" eq 'limit' ||
56                         "$args[$ind]" eq 'offset' ||
57                         "$args[$ind]" eq 'timeout' ) {
58
59                         my $key_ind = $ind;
60                         $ind++;
61                         my $value_ind = $ind;
62                         $cache_args{$args[$key_ind]} = $args[$value_ind];
63                         $log->debug("Cache limiter value for $args[$key_ind] is $args[$value_ind]", INTERNAL);
64                         next;
65                 }
66                 $key_string .= $args[$ind];
67                 $log->debug("Partial cache key value is $args[$ind]", INTERNAL);
68                 push @real_args, $args[$ind];
69         }
70
71         my $cache_key = md5_hex($key_string);
72         $log->debug("Key string for cache lookup is $key_string -> $cache_key", DEBUG);
73
74         my $cached_res = OpenSRF::Utils::Cache->new->get_cache( $cache_key );
75         if (defined $cached_res) {
76                 $log->debug("Found ".scalar(@$cached_res)." records in the cache", INFO);
77                 $log->debug("Values from cache: ".join(', ', @$cached_res), INTERNAL);
78                 $client->respond( $_ ) for ( grep { defined } @$cached_res[$cache_args{offset} .. int($cache_args{offset} + $cache_args{limit} - 1)] );
79                 return undef;
80         }
81
82         my $method = $self->method_lookup($self->{real_api_name});
83         my @res = $method->run(@real_args);
84
85
86         $client->respond( $_ ) for ( grep { defined } @res[$cache_args{offset} .. int($cache_args{offset} + $cache_args{limit} - 1)] );
87
88         OpenSRF::Utils::Cache->new->put_cache( $cache_key => \@res => $cache_args{timeout});
89
90         return undef;
91 }
92
93 sub retrieve_node {
94         my $self = shift;
95         my $client = shift;
96         my @ids = @_;
97
98         my $cdbi = $self->{cdbi};
99
100         for my $id ( @ids ) {
101                 next unless ($id);
102
103                 my ($rec) = $cdbi->fast_fieldmapper($id);
104                 $client->respond( $rec ) if ($rec);
105
106                 last if ($self->api_name !~ /batch/o);
107         }
108         return undef;
109 }
110
111 sub search {
112         my $self = shift;
113         my $client = shift;
114         my $searches = shift;
115
116         my $cdbi = $self->{cdbi};
117
118         $log->debug("Searching $cdbi for { ".join(',', map { "$_ => $$searches{$_}" } keys %$searches).' }',DEBUG);
119
120         for my $obj ($cdbi->search($searches)) {
121                 $client->respond( $obj->to_fieldmapper );
122         }
123         return undef;
124 }
125
126 sub search_one_field {
127         my $self = shift;
128         my $client = shift;
129         my @terms = @_;
130
131         (my $search_type = $self->api_name) =~ s/.*\.(search[^.]*).*/$1/o;
132         (my $col = $self->api_name) =~ s/.*\.$search_type\.([^.]+).*/$1/;
133         my $cdbi = $self->{cdbi};
134
135         my $like = 0;
136         $like = 1 if ($search_type =~ /like$/o);
137
138         for my $term (@terms) {
139                 $log->debug("Searching $cdbi for $col using type $search_type, value '$term'",DEBUG);
140                 $client->respond( [ $cdbi->fast_fieldmapper($term,$col,$like) ] );
141         }
142         return undef;
143 }
144
145
146 sub create_node {
147         my $self = shift;
148         my $client = shift;
149         my $node = shift;
150
151         my $cdbi = $self->{cdbi};
152
153         my $success;
154         try {
155                 my $rec = $cdbi->create($node);
156                 $success = $rec->id if ($rec);
157         } catch Error with {
158                 $success = 0;
159         };
160
161         return $success;
162 }
163
164 sub update_node {
165         my $self = shift;
166         my $client = shift;
167         my $node = shift;
168
169         my $cdbi = $self->{cdbi};
170
171         return $cdbi->update($node);
172 }
173
174 sub mass_delete {
175         my $self = shift;
176         my $client = shift;
177         my $search = shift;
178
179         my $where = 'WHERE ';
180
181         my $cdbi = $self->{cdbi};
182         my $table = $cdbi->table;
183
184         my @keys = sort keys %$search;
185         
186         my @binds;
187         my @wheres;
188         for my $col ( @keys ) {
189                 if (ref($$search{$col}) and ref($$search{$col}) =~ /ARRAY/o) {
190                         push @wheres, "$col IN (" . join(',', map { '?' } @{ $$search{$col} }) . ')';
191                         push @binds, map { "$_" } @{ $$search{$col} };
192                 } else {
193                         push @wheres, "$col = ?";
194                         push @binds, $$search{$col};
195                 }
196         }
197         $where .= join ' AND ', @wheres;
198
199         my $delete = "DELETE FROM $table $where";
200
201         $log->debug("Performing MASS deletion : $delete",DEBUG);
202
203         my $dbh = $cdbi->db_Main;
204         my $success = 1;
205         try {
206                 my $sth = $dbh->prepare($delete);
207                 $sth->execute( @binds );
208                 $sth->finish;
209                 $log->debug("MASS Delete succeeded",DEBUG);
210         } catch Error with {
211                 $log->debug("MASS Delete FAILED : ".shift(),DEBUG);
212                 $success = 0;
213         };
214         return $success;
215 }
216
217 sub delete_node {
218         my $self = shift;
219         my $client = shift;
220         my $node = shift;
221
222         my $cdbi = $self->{cdbi};
223
224         my $success = 1;
225         try {
226                 $success = $cdbi->delete($node);
227         } catch Error with {
228                 $success = 0;
229         };
230         return $success;
231 }
232
233 sub batch_call {
234         my $self = shift;
235         my $client = shift;
236         my @nodes = @_;
237
238         my $cdbi = $self->{cdbi};
239         my $api_name = $self->api_name;
240         (my $single_call_api_name = $api_name) =~ s/batch\.//o;
241
242         $log->debug("Default $api_name looking up $single_call_api_name...",INTERNAL);
243         my $method = $self->method_lookup($single_call_api_name);
244
245         my @success;
246         while ( my $node = shift(@nodes) ) {
247                 my ($res) = $method->run( $node ); 
248                 push(@success, 1) if ($res >= 0);
249         }
250
251         my $insert_total = 0;
252         $insert_total += $_ for (@success);
253
254         return $insert_total;
255 }
256
257 eval '
258 use OpenILS::Application::Storage::Publisher::actor;
259 use OpenILS::Application::Storage::Publisher::action;
260 use OpenILS::Application::Storage::Publisher::asset;
261 use OpenILS::Application::Storage::Publisher::biblio;
262 use OpenILS::Application::Storage::Publisher::config;
263 use OpenILS::Application::Storage::Publisher::metabib;
264 ';
265
266 for my $fmclass ( Fieldmapper->classes ) {
267         (my $cdbi = $fmclass) =~ s/^Fieldmapper:://o;
268         (my $class = $cdbi) =~ s/::.*//o;
269         (my $api_class = $cdbi) =~ s/::/./go;
270         my $registration_class = __PACKAGE__ . "::$class";
271         my $api_prefix = 'open-ils.storage.direct.'.$api_class;
272
273         # Create the search method
274         unless ( __PACKAGE__->is_registered( $api_prefix.'.search' ) ) {
275                 __PACKAGE__->register_method(
276                         api_name        => $api_prefix.'.search',
277                         method          => 'search',
278                         api_level       => 1,
279                         stream          => 1,
280                         cdbi            => $cdbi,
281                         cachable        => 1,
282                 );
283         }
284
285         # Create the retrieve method
286         unless ( __PACKAGE__->is_registered( $api_prefix.'.retrieve' ) ) {
287                 __PACKAGE__->register_method(
288                         api_name        => $api_prefix.'.retrieve',
289                         method          => 'retrieve_node',
290                         api_level       => 1,
291                         cdbi            => $cdbi,
292                         cachable        => 1,
293                 );
294         }
295
296         # Create the batch retrieve method
297         unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.retrieve' ) ) {
298                 __PACKAGE__->register_method(
299                         api_name        => $api_prefix.'.batch.retrieve',
300                         method          => 'retrieve_node',
301                         api_level       => 1,
302                         stream          => 1,
303                         cdbi            => $cdbi,
304                         cachable        => 1,
305                 );
306         }
307
308         for my $field ($fmclass->real_fields) {
309                 unless ( __PACKAGE__->is_registered( $api_prefix.'.search.'.$field ) ) {
310                         __PACKAGE__->register_method(
311                                 api_name        => $api_prefix.'.search.'.$field,
312                                 method          => 'search_one_field',
313                                 api_level       => 1,
314                                 cdbi            => $cdbi,
315                                 cachable        => 1,
316                         );
317                 }
318                 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_like.'.$field ) ) {
319                         __PACKAGE__->register_method(
320                                 api_name        => $api_prefix.'.search_like.'.$field,
321                                 method          => 'search_one_field',
322                                 api_level       => 1,
323                                 cdbi            => $cdbi,
324                                 cachable        => 1,
325                         );
326                 }
327         }
328
329
330         # Create the create method
331         unless ( __PACKAGE__->is_registered( $api_prefix.'.create' ) ) {
332                 __PACKAGE__->register_method(
333                         api_name        => $api_prefix.'.create',
334                         method          => 'create_node',
335                         api_level       => 1,
336                         cdbi            => $cdbi,
337                 );
338         }
339
340         # Create the batch create method
341         unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.create' ) ) {
342                 __PACKAGE__->register_method(
343                         api_name        => $api_prefix.'.batch.create',
344                         method          => 'batch_call',
345                         api_level       => 1,
346                         cdbi            => $cdbi,
347                 );
348         }
349
350         # Create the update method
351         unless ( __PACKAGE__->is_registered( $api_prefix.'.update' ) ) {
352                 __PACKAGE__->register_method(
353                         api_name        => $api_prefix.'.update',
354                         method          => 'update_node',
355                         api_level       => 1,
356                         cdbi            => $cdbi,
357                 );
358         }
359
360         # Create the batch update method
361         unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.update' ) ) {
362                 __PACKAGE__->register_method(
363                         api_name        => $api_prefix.'.batch.update',
364                         method          => 'batch_call',
365                         api_level       => 1,
366                         cdbi            => $cdbi,
367                 );
368         }
369
370         # Create the delete method
371         unless ( __PACKAGE__->is_registered( $api_prefix.'.delete' ) ) {
372                 __PACKAGE__->register_method(
373                         api_name        => $api_prefix.'.delete',
374                         method          => 'delete_node',
375                         api_level       => 1,
376                         cdbi            => $cdbi,
377                 );
378         }
379
380         # Create the batch delete method
381         unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.delete' ) ) {
382                 __PACKAGE__->register_method(
383                         api_name        => $api_prefix.'.batch.delete',
384                         method          => 'batch_call',
385                         api_level       => 1,
386                         cdbi            => $cdbi,
387                 );
388         }
389
390         # Create the search-based mass delete method
391         unless ( __PACKAGE__->is_registered( $api_prefix.'.mass_delete' ) ) {
392                 __PACKAGE__->register_method(
393                         api_name        => $api_prefix.'.mass_delete',
394                         method          => 'mass_delete',
395                         api_level       => 1,
396                         cdbi            => $cdbi,
397                 );
398         }
399
400 }
401
402 1;