]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/lib/OpenILS/WWW/AddedContent.pm
LP#845763 remote added content breaks
[working/Evergreen.git] / Open-ILS / src / perlmods / lib / OpenILS / WWW / AddedContent.pm
1 package OpenILS::WWW::AddedContent;
2 use strict; use warnings;
3
4 use CGI;
5 use Apache2::Log;
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;
11 use Data::Dumper;
12 use UNIVERSAL::require;
13
14 use OpenSRF::EX qw(:try);
15 use OpenSRF::Utils::Cache;
16 use OpenSRF::System;
17 use OpenSRF::Utils::Logger qw/$logger/;
18
19 use LWP::UserAgent;
20 use MIME::Base64;
21
22 my $AC = __PACKAGE__;
23
24
25 # set the bootstrap config when this module is loaded
26 my $bs_config;
27
28 sub import {
29     my $self = shift;
30     $bs_config = shift;
31 }
32
33
34 my $handler; # added content handler class handle
35 my $cache; # memcache handle
36 my $net_timeout; # max seconds to wait for a response from the added content vendor
37 my $max_errors; # max consecutive lookup failures before added content is temporarily disabled
38 my $error_countdown; # current consecutive errors countdown
39
40 # number of seconds to wait before next lookup 
41 # is attempted after lookups have been disabled
42 my $error_retry_timeout;
43
44
45 sub child_init {
46
47     OpenSRF::System->bootstrap_client( config_file => $bs_config );
48     $cache = OpenSRF::Utils::Cache->new;
49
50     my $sclient = OpenSRF::Utils::SettingsClient->new();
51     my $ac_data = $sclient->config_value("added_content");
52
53     return unless $ac_data;
54     my $ac_handler = $ac_data->{module};
55     return unless $ac_handler;
56
57     $net_timeout = $ac_data->{timeout} || 1;
58     $error_countdown = $max_errors = $ac_data->{max_errors} || 10;
59     $error_retry_timeout = $ac_data->{retry_timeout} || 600;
60
61     $logger->debug("Attempting to load Added Content handler: $ac_handler");
62
63     $ac_handler->use;
64
65     if($@) {    
66         $logger->error("Unable to load Added Content handler [$ac_handler]: $@"); 
67         return; 
68     }
69
70     $handler = $ac_handler->new($ac_data);
71     $logger->debug("added content loaded handler: $handler");
72 }
73
74
75 sub handler {
76
77     my $r   = shift;
78     return Apache2::Const::DECLINED if (-e $r->filename);
79
80     my $cgi = CGI->new;
81     my @path_parts = split( /\//, $r->unparsed_uri );
82     my $type = $path_parts[-3];
83     my $format = $path_parts[-2];
84     my $key = $path_parts[-1];
85     my $res;
86
87     child_init() unless $handler;
88
89     return Apache2::Const::NOT_FOUND unless $handler and $type and $format and $key;
90
91     my $err;
92     my $data;
93     my $method = "${type}_${format}";
94
95     return Apache2::Const::NOT_FOUND unless $handler->can($method);
96     return $res if defined($res = $AC->serve_from_cache($type, $format, $key));
97     return Apache2::Const::NOT_FOUND unless $AC->lookups_enabled;
98
99     try {
100         $data = $handler->$method($key);
101     } catch Error with { 
102         $err = shift; 
103         decr_error_countdown();
104         $logger->debug("added content handler failed: $method($key) => $err");
105     };
106
107     return Apache2::Const::NOT_FOUND if $err;
108
109     if(!$data) {
110         # if the AC lookup found no corresponding data, cache that information
111         $logger->debug("added content handler returned no results $method($key)") unless $data;
112         $AC->cache_result($type, $format, $key, {nocontent=>1});
113         return Apache2::Const::NOT_FOUND;
114     }
115     
116     $AC->print_content($data);
117     $AC->cache_result($type, $format, $key, $data);
118
119     reset_error_countdown();
120     return Apache2::Const::OK;
121 }
122
123 sub print_content {
124     my($class, $data, $from_cache) = @_;
125     return Apache2::Const::NOT_FOUND if $data->{nocontent};
126
127     my $ct = $data->{content_type};
128     my $content = $data->{content};
129     print "Content-type: $ct\n\n";
130
131     if($data->{binary}) {
132         binmode STDOUT;
133         # if it hasn't been cached yet, it's still in binary form
134         print( ($from_cache) ? decode_base64($content) : $content );
135     } else {
136         print $content;
137     }
138
139
140     return Apache2::Const::OK;
141 }
142
143
144
145
146 # returns an HTPP::Response object
147 sub get_url {
148     my( $self, $url ) = @_;
149
150     $logger->info("added content getting [timeout=$net_timeout, errors_remaining=$error_countdown] URL = $url");
151     my $agent = LWP::UserAgent->new(timeout => $net_timeout);
152
153     my $res = $agent->get($url); 
154     $logger->info("added content request returned with code " . $res->code);
155     die "added content request failed: " . $res->status_line ."\n" unless $res->is_success;
156
157     return $res;
158 }
159
160 sub lookups_enabled {
161     if( $cache->get_cache('ac.no_lookup') ) {
162         $logger->info("added content lookup disabled");
163         return undef;
164     }
165     return 1;
166 }
167
168 sub disable_lookups {
169     $cache->put_cache('ac.no_lookup', 1, $error_retry_timeout);
170 }
171
172 sub decr_error_countdown {
173     $error_countdown--;
174     if($error_countdown < 1) {
175         $logger->warn("added content error count exhausted.  Disabling lookups for $error_retry_timeout seconds");
176         $AC->disable_lookups;
177     }
178 }
179
180 sub reset_error_countdown {
181     $error_countdown = $max_errors;
182 }
183
184 sub cache_result {
185     my($class, $type, $format, $key, $data) = @_;
186     $logger->debug("caching $type/$format/$key");
187     $data->{content} = encode_base64($data->{content}) if $data->{binary};
188     return $cache->put_cache("ac.$type.$format.$key", $data);
189 }
190
191 sub serve_from_cache {
192     my($class, $type, $format, $key) = @_;
193     my $data = $cache->get_cache("ac.$type.$format.$key");
194     return undef unless $data;
195     $logger->debug("serving $type/$format/$key from cache");
196     return $class->print_content($data, 1);
197 }
198
199
200
201 1;