]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/lib/OpenILS/Application/ResolverResolver.pm
Add OILS_SIP_MSG_BILL_ERR for when an error occurs getting bills.
[working/Evergreen.git] / Open-ILS / src / perlmods / lib / OpenILS / Application / ResolverResolver.pm
1 #!/usr/bin/perl
2
3 # Copyright (C) 2009-2010 Dan Scott <dscott@laurentian.ca>
4
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
9
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18
19 =head1 NAME
20
21 OpenILS::Application::ResolverResolver - retrieves holdings from OpenURL resolvers
22
23 =head1 SYNOPSIS
24
25 Via srfsh:
26   request open-ils.resolver open-ils.resolver.resolve_holdings "issn", "0022-362X"
27 or:
28   request open-ils.resolver open-ils.resolver.resolve_holdings.raw "issn", "0022-362X"
29
30 Via Perl:
31   my $session = OpenSRF::AppSession->create("open-ils.resolver");
32   my $request = $session->request("open-ils.resolver.resolve_holdings", [ "issn", "0022-362X" ] )->gather();
33   $session->disconnect();
34
35   # $request is a reference to the list of hashes
36
37 =head1 DESCRIPTION
38
39 OpenILS::Application::ResolverResolver caches responses from OpenURL resolvers
40 to requests for full-text holdings. Currently integration with SFX is supported.
41
42 Each org_unit can specify a different base URL as the third argument to
43 resolve_holdings(). Eventually org_units will have org_unit settings to hold
44 their resolver type and base URL.
45
46 =head1 AUTHOR
47
48 Dan Scott, dscott@laurentian.ca
49
50 =cut
51
52 package OpenILS::Application::ResolverResolver;
53
54 use strict;
55 use warnings;
56 use LWP::UserAgent;
57 use XML::LibXML;
58
59 # All OpenSRF applications must be based on OpenSRF::Application or
60 # a subclass thereof.  Makes sense, eh?
61 use OpenILS::Application;
62 use base qw/OpenILS::Application/;
63
64 # This is the client class, used for connecting to open-ils.storage
65 use OpenSRF::AppSession;
66
67 # This is an extension of Error.pm that supplies some error types to throw
68 use OpenSRF::EX qw(:try);
69
70 # This is a helper class for querying the OpenSRF Settings application ...
71 use OpenSRF::Utils::SettingsClient;
72
73 # ... and here we have the built in logging helper ...
74 use OpenSRF::Utils::Logger qw($logger);
75
76 # ... and this manages cached results for us ...
77 use OpenSRF::Utils::Cache;
78
79 # ... and this gives us access to the Fieldmapper
80 use OpenILS::Utils::Fieldmapper;
81
82 my $prefix = "open-ils.resolver_"; # Prefix for caching values
83 my $cache;
84 my $cache_timeout;
85 my $default_url_base;              # Default resolver location
86
87 our ($ua, $parser);
88
89
90 sub initialize {
91     $cache = OpenSRF::Utils::Cache->new('global');
92     my $sclient = OpenSRF::Utils::SettingsClient->new();
93     $cache_timeout = $sclient->config_value(
94         "apps", "open-ils.resolver", "app_settings", "cache_timeout" ) || 300;
95     $default_url_base = $sclient->config_value(
96         "apps", "open-ils.resolver", "app_settings", "default_url_base");
97 }
98
99 sub child_init {
100     # We need a User Agent to speak to the SFX beast
101     $ua = new LWP::UserAgent;
102     $ua->agent('SameOrigin/1.0');
103
104     # SFX returns XML to us; let us parse
105     $parser = new XML::LibXML;
106 }
107
108 sub resolve_holdings {
109     my $self = shift;
110     my $conn = shift;
111     my $id_type = shift;      # keep it simple for now, either 'issn' or 'isbn'
112     my $id_value = shift;     # the normalized ISSN or ISBN
113     my $url_base = shift || $default_url_base; 
114
115     # We'll use this in our cache key
116     my $method = $self->api_name;
117
118     # We might want to return raw JSON for speedier responses
119     my $format = 'fieldmapper';
120     if ($self->api_name =~ /raw$/) {
121         $format = 'raw';
122     }
123
124     # Big ugly SFX OpenURL request
125     my $url_args = '?url_ver=Z39.88-2004&url_ctx_fmt=infofi/fmt:kev:mtx:ctx&'
126         . 'ctx_enc=UTF-8&ctx_ver=Z39.88-2004&rfr_id=info:sid/evergreen&'
127         . 'sfx.ignore_date_threshold=1&'
128         . 'sfx.response_type=multi_obj_detailed_xml&__service_type=getFullTxt';
129
130     if ($id_type eq 'issn') {
131         $url_args .= "&rft.issn=$id_value";
132     } elsif ($id_type eq 'isbn') {
133         $url_args .= "&rft.isbn=$id_value";
134     }
135     
136     my $ckey = $prefix . $method . $url_base . $id_type . $id_value;
137
138     # Check the cache to see if we've already looked this up
139     # If we have, shortcut our return value
140     my $result = $cache->get_cache($ckey) || undef;
141     if ($result) {
142         $logger->info("Resolver found a cache hit");    
143         return $result;
144     }
145
146     # Otherwise, let's go and grab the info from the SFX server
147     my $req = HTTP::Request->new('GET', "$url_base$url_args");
148
149     # Let's see what we we're trying to request
150     $logger->info("Resolving the following request: $url_base$url_args");
151
152     my $res = $ua->request($req);
153
154     my $xml = $res->content;
155     my $parsed_sfx = $parser->parse_string($xml);
156
157     my (@targets) = $parsed_sfx->findnodes('//target');
158
159     my @sfx_result;
160     foreach my $target (@targets) {
161         my %full_txt;
162
163         # Ensure we have a name and especially URL to return
164         $full_txt{'name'} = $target->findvalue('./target_public_name') || next;
165         $full_txt{'url'} = $target->findvalue('.//target_url') || next;
166         $full_txt{'coverage'} = $target->findvalue('.//coverage_statement') || '';
167         $full_txt{'embargo'} = $target->findvalue('.//embargo_statement') || '';
168
169         if ($format eq 'raw') {
170             push @sfx_result, {
171                 public_name => $full_txt{'name'},
172                 target_url => $full_txt{'url'},
173                 target_coverage => $full_txt{'coverage'},
174                 target_embargo => $full_txt{'embargo'},
175             };
176         } else {
177             my $rhr = Fieldmapper::resolver::holdings_record->new;
178             $rhr->public_name($full_txt{'name'});
179             $rhr->target_url($full_txt{'url'});
180             $rhr->target_coverage($full_txt{'coverage'});
181             $rhr->target_embargo($full_txt{'embargo'});
182             push @sfx_result, $rhr;
183         }
184     }
185
186     # Stuff this into the cache
187     $cache->put_cache($ckey, \@sfx_result, $cache_timeout);
188     
189     # Don't return the list unless it contains results
190     if (scalar(@sfx_result)) {
191         return \@sfx_result;
192     }
193
194     return undef;
195 }
196
197 __PACKAGE__->register_method(
198     method    => 'resolve_holdings',
199     api_name  => 'open-ils.resolver.resolve_holdings',
200     api_level => 1,
201     argc      => 3,
202     signature => {
203         desc     => <<"         DESC",
204 Returns a list of "rhr" objects representing the full-text holdings for a given ISBN or ISSN
205          DESC
206         'params' => [ {
207                 name => 'id_type',
208                 desc => 'The type of identifier ("issn" or "isbn")',
209                 type => 'string' 
210             }, {
211                 name => 'id_value',
212                 desc => 'The identifier value',
213                 type => 'string'
214             }, {
215                  name => 'url_base',
216                  desc => 'The base URL for the resolver and instance',
217                  type => 'string'
218             },
219         ],
220         'return' => {
221             desc => 'Returns a list of "rhr" objects representing the full-text holdings for a given ISBN or ISSN',
222             type => 'array'
223         }
224     }
225 );
226
227 __PACKAGE__->register_method(
228     method    => 'resolve_holdings',
229     api_name  => 'open-ils.resolver.resolve_holdings.raw',
230     api_level => 1,
231     argc      => 3,
232     signature => {
233         desc     => <<"         DESC",
234 Returns a list of raw JSON objects representing the full-text holdings for a given ISBN or ISSN
235          DESC
236         'params' => [ {
237                 name => 'id_type',
238                 desc => 'The type of identifier ("issn" or "isbn")',
239                 type => 'string' 
240             }, {
241                 name => 'id_value',
242                 desc => 'The identifier value',
243                 type => 'string'
244             }, {
245                  name => 'url_base',
246                  desc => 'The base URL for the resolver and instance',
247                  type => 'string'
248             },
249         ],
250         'return' => {
251             desc => 'Returns a list of raw JSON objects representing the full-text holdings for a given ISBN or ISSN',
252             type => 'array'
253         }
254     }
255 );
256
257 # Clear cache for specific lookups
258 sub delete_cached_holdings {
259     my $self = shift;
260     my $conn = shift;
261     my $id_type = shift;      # keep it simple for now, either 'issn' or 'isbn'
262     my $id_value = shift;     # the normalized ISSN or ISBN
263     my $url_base = shift || $default_url_base; 
264     my @deleted_keys;
265
266     $logger->warn("Deleting value [$id_value]");
267     # We'll use this in our cache key
268     foreach my $method ('open-ils.resolver.resolve_holdings.raw', 'open-ils.resolver.resolve_holdings') {
269         my $ckey = $prefix . $method . $url_base . $id_type . $id_value;
270
271         $logger->warn("Deleted cache key [$ckey]");
272         my $result = $cache->delete_cache($ckey);
273
274         $logger->warn("Result of deleting cache key: [$result]");
275         push @deleted_keys, $result;
276     }
277
278     return \@deleted_keys;
279 }
280
281 __PACKAGE__->register_method(
282     method    => 'delete_cached_holdings',
283     api_name  => 'open-ils.resolver.delete_cached_holdings',
284     api_level => 1,
285     argc      => 3,
286     signature => {
287         desc     => <<"         DESC",
288 Deletes the cached value of the full-text holdings for a given ISBN or ISSN
289          DESC
290         'params' => [ {
291                 name => 'id_type',
292                 desc => 'The type of identifier ("issn" or "isbn")',
293                 type => 'string' 
294             }, {
295                 name => 'id_value',
296                 desc => 'The identifier value',
297                 type => 'string'
298             }, {
299                  name => 'url_base',
300                  desc => 'The base URL for the resolver and instance',
301                  type => 'string'
302             },
303         ],
304         'return' => {
305             desc => 'Deletes the cached value of the full-text holdings for a given ISBN or ISSN',
306             type => 'array'
307         }
308     }
309 );
310
311
312 1;