1 package OpenILS::WWW::AddedContent;
2 use strict; use warnings;
6 use Apache2::Const -compile => qw(OK REDIRECT DECLINED NOT_FOUND :log);
7 use APR::Const -compile => qw(:error SUCCESS);
8 use Apache2::RequestRec ();
9 use Apache2::RequestIO ();
10 use Apache2::RequestUtil;
12 use UNIVERSAL::require;
14 use OpenSRF::EX qw(:try);
15 use OpenSRF::Utils::Cache;
17 use OpenSRF::Utils::Logger qw/$logger/;
18 use OpenILS::Utils::CStoreEditor;
26 # set the bootstrap config when this module is loaded
35 my $handler; # added content handler class handle
36 my $cache; # memcache handle
37 my $net_timeout; # max seconds to wait for a response from the added content vendor
38 my $max_errors; # max consecutive lookup failures before added content is temporarily disabled
39 my $error_countdown; # current consecutive errors countdown
41 # number of seconds to wait before next lookup
42 # is attempted after lookups have been disabled
43 my $error_retry_timeout;
48 OpenSRF::System->bootstrap_client( config_file => $bs_config );
49 $cache = OpenSRF::Utils::Cache->new;
51 my $sclient = OpenSRF::Utils::SettingsClient->new();
52 my $ac_data = $sclient->config_value("added_content");
54 return Apache2::Const::OK unless $ac_data;
55 my $ac_handler = $ac_data->{module};
56 return Apache2::Const::OK unless $ac_handler;
58 $net_timeout = $ac_data->{timeout} || 1;
59 $error_countdown = $max_errors = $ac_data->{max_errors} || 10;
60 $error_retry_timeout = $ac_data->{retry_timeout} || 600;
62 $logger->debug("Attempting to load Added Content handler: $ac_handler");
67 $logger->error("Unable to load Added Content handler [$ac_handler]: $@");
68 return Apache2::Const::OK;
71 $handler = $ac_handler->new($ac_data);
72 $logger->debug("added content loaded handler: $handler");
73 return Apache2::Const::OK;
80 return Apache2::Const::DECLINED if (-e $r->filename);
83 my @path_parts = split( /\//, $r->unparsed_uri );
84 my $type = $path_parts[-3];
85 my $format = $path_parts[-2];
86 my $id = $path_parts[-1];
89 child_init() unless $handler;
91 return Apache2::Const::NOT_FOUND unless $handler and $type and $format and $id;
95 my $method = "${type}_${format}";
97 return Apache2::Const::NOT_FOUND unless $handler->can($method);
98 return $res if defined($res = $AC->serve_from_cache($type, $format, $id));
99 return Apache2::Const::NOT_FOUND unless $AC->lookups_enabled;
101 my $key_data = get_rec_keys($id);
102 my @isbns = grep {$_->{tag} eq '020'} @$key_data;
103 my @upcs = grep {$_->{tag} eq '024'} @$key_data;
106 isbn => [map {$_->{value}} @isbns],
107 upc => [map {$_->{value}} @upcs]
113 use OpenSRF::Utils::JSON;
114 $logger->info("Added Content Keys: " . OpenSRF::Utils::JSON->perl2JSON($keys));
117 $data = $handler->$method($keys);
120 decr_error_countdown();
121 $logger->debug("added content handler failed: $method($id) => $err");
124 return Apache2::Const::NOT_FOUND if $err;
128 # if the AC lookup found no corresponding data, cache that information
129 $logger->debug("added content handler returned no results $method($id)") unless $data;
130 $AC->cache_result($type, $format, $id, {nocontent=>1});
131 return Apache2::Const::NOT_FOUND;
134 $AC->print_content($data);
135 $AC->cache_result($type, $format, $id, $data);
137 reset_error_countdown();
138 return Apache2::Const::OK;
141 # returns [{tag => $tag, value => $value}, {tag => $tag2, value => $value2}]
144 return OpenILS::Utils::CStoreEditor->new->json_query({
145 select => {mfr => ['tag', 'value']},
168 my($class, $data, $from_cache) = @_;
169 return Apache2::Const::NOT_FOUND if $data->{nocontent};
171 my $ct = $data->{content_type};
172 my $content = $data->{content};
173 print "Content-type: $ct\n\n";
175 if($data->{binary}) {
177 # if it hasn't been cached yet, it's still in binary form
178 print( ($from_cache) ? decode_base64($content) : $content );
184 return Apache2::Const::OK;
190 # returns an HTPP::Response object
192 my( $self, $url ) = @_;
194 $logger->info("added content getting [timeout=$net_timeout, errors_remaining=$error_countdown] URL = $url");
195 my $agent = LWP::UserAgent->new(timeout => $net_timeout);
197 my $res = $agent->get($url);
198 $logger->info("added content request returned with code " . $res->code);
199 die "added content request failed: " . $res->status_line ."\n" unless $res->is_success;
204 sub lookups_enabled {
205 if( $cache->get_cache('ac.no_lookup') ) {
206 $logger->info("added content lookup disabled");
212 sub disable_lookups {
213 $cache->put_cache('ac.no_lookup', 1, $error_retry_timeout);
216 sub decr_error_countdown {
218 if($error_countdown < 1) {
219 $logger->warn("added content error count exhausted. Disabling lookups for $error_retry_timeout seconds");
220 $AC->disable_lookups;
224 sub reset_error_countdown {
225 $error_countdown = $max_errors;
229 my($class, $type, $format, $key, $data) = @_;
230 $logger->debug("caching $type/$format/$key");
231 $data->{content} = encode_base64($data->{content}) if $data->{binary};
232 return $cache->put_cache("ac.$type.$format.$key", $data);
235 sub serve_from_cache {
236 my($class, $type, $format, $key) = @_;
237 my $data = $cache->get_cache("ac.$type.$format.$key");
238 return undef unless $data;
239 $logger->debug("serving $type/$format/$key from cache");
240 return $class->print_content($data, 1);