[open-ils-commits] r18666 - in trunk/Open-ILS: examples src/perlmods/OpenILS/Application (dbs)

svn at svn.open-ils.org svn at svn.open-ils.org
Mon Nov 8 16:21:36 EST 2010


Author: dbs
Date: 2010-11-08 16:21:31 -0500 (Mon, 08 Nov 2010)
New Revision: 18666

Added:
   trunk/Open-ILS/src/perlmods/OpenILS/Application/ResolverResolver.pm
Modified:
   trunk/Open-ILS/examples/fm_IDL.xml
   trunk/Open-ILS/examples/opensrf.xml.example
   trunk/Open-ILS/examples/opensrf_core.xml.example
Log:
Add the OpenURL resolver caching service as an optional component

Currently limited to supporting the Ex Libris SFX OpenURL resolver,
this caches the results of looking up full-text availability of
resources by ISBN or ISSN.

For an example of incorporating this service into your OPAC, see
http://svn.open-ils.org/trac/ILS-Contrib/browser/conifer/branches/rel_1_6_1/web/opac/skin/lul/xml/result/result_table.xml#L102
for returning the full-text availability of a given resource by ISSN.


Modified: trunk/Open-ILS/examples/fm_IDL.xml
===================================================================
--- trunk/Open-ILS/examples/fm_IDL.xml	2010-11-08 21:18:40 UTC (rev 18665)
+++ trunk/Open-ILS/examples/fm_IDL.xml	2010-11-08 21:21:31 UTC (rev 18666)
@@ -128,6 +128,14 @@
 		</fields>
 	</class>
 
+	<class id="rhr" oils_obj:fieldmapper="resolver::holdings_record" oils_persist:virtual="true">
+		<fields>
+			<field name="public_name" oils_persist:virtual="true" />
+			<field name="target_url" oils_persist:virtual="true" />
+			<field name="target_coverage" oils_persist:virtual="true" />
+			<field name="target_embargo" oils_persist:virtual="true" />
+		</fields>
+	</class>
 
 	<!-- Actually in the DB -->
 	<class id="vmp" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="vandelay::merge_profile" oils_persist:tablename="vandelay.merge_profile" reporter:label="Bib Import Merge Profile">

Modified: trunk/Open-ILS/examples/opensrf.xml.example
===================================================================
--- trunk/Open-ILS/examples/opensrf.xml.example	2010-11-08 21:18:40 UTC (rev 18665)
+++ trunk/Open-ILS/examples/opensrf.xml.example	2010-11-08 21:21:31 UTC (rev 18666)
@@ -1007,6 +1007,30 @@
                 </app_settings>
             </open-ils.reporter-store>
 
+<!--
+           <open-ils.resolver>
+               <keepalive>3</keepalive>
+               <stateless>1</stateless>
+               <language>perl</language>
+               <implementation>OpenILS::Application::ResolverResolver</implementation>
+               <max_requests>93</max_requests>
+               <unix_config>
+                  <unix_sock>open-ils.resolver_unix.sock</unix_sock>
+                  <unix_pid>open-ils.resolver_unix.pid</unix_pid>
+                  <max_requests>1000</max_requests>
+                  <unix_log>open-ils.resolver_unix.log</unix_log>
+                  <min_children>5</min_children>
+                  <max_children>15</max_children>
+                  <min_spare_children>3</min_spare_children>
+                  <max_spare_children>5</max_spare_children>
+               </unix_config>
+               <app_settings>
+                  <cache_timeout>86400</cache_timeout>
+                  <default_url_base>http://path/to/sfx</default_url_base>
+               </app_settings>
+            </open-ils.resolver>
+-->
+
             <open-ils.permacrud>
                <keepalive>3</keepalive>
                <stateless>1</stateless>
@@ -1125,6 +1149,7 @@
                 <appname>open-ils.ingest</appname>  
                 <appname>open-ils.reporter</appname>  
                 <appname>open-ils.reporter-store</appname>  
+                <appname>open-ils.resolver</appname>  
                 <appname>open-ils.permacrud</appname>  
                 <appname>open-ils.pcrud</appname>  
                 <appname>open-ils.trigger</appname>  

Modified: trunk/Open-ILS/examples/opensrf_core.xml.example
===================================================================
--- trunk/Open-ILS/examples/opensrf_core.xml.example	2010-11-08 21:18:40 UTC (rev 18665)
+++ trunk/Open-ILS/examples/opensrf_core.xml.example	2010-11-08 21:21:31 UTC (rev 18666)
@@ -30,6 +30,7 @@
           <service>open-ils.pcrud</service>
           <service>open-ils.permacrud</service>
           <service>open-ils.reporter</service>
+          <service>open-ils.resolver</service>
           <service>open-ils.search</service>
           <service>open-ils.supercat</service>
           <service>open-ils.vandelay</service>

Added: trunk/Open-ILS/src/perlmods/OpenILS/Application/ResolverResolver.pm
===================================================================
--- trunk/Open-ILS/src/perlmods/OpenILS/Application/ResolverResolver.pm	                        (rev 0)
+++ trunk/Open-ILS/src/perlmods/OpenILS/Application/ResolverResolver.pm	2010-11-08 21:21:31 UTC (rev 18666)
@@ -0,0 +1,304 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2009-2010 Dan Scott <dscott at laurentian.ca>
+
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+=head1 NAME
+
+OpenILS::Application::ResolverResolver - retrieves holdings from OpenURL resolvers
+
+=head1 SYNOPSIS
+
+Via srfsh:
+  request open-ils.resolver open-ils.resolver.resolve_holdings "issn", "0022-362X"
+or:
+  request open-ils.resolver open-ils.resolver.resolve_holdings.raw "issn", "0022-362X"
+
+Via Perl:
+  my $session = OpenSRF::AppSession->create("open-ils.resolver");
+  my $request = $session->request("open-ils.resolver.resolve_holdings", [ "issn", "0022-362X" ] )->gather();
+  $session->disconnect();
+
+  # $request is a reference to the list of hashes
+
+=head1 DESCRIPTION
+
+OpenILS::Application::ResolverResolver caches responses from OpenURL resolvers
+to requests for full-text holdings. Currently integration with SFX is supported.
+
+Each org_unit can specify a different base URL as the third argument to
+resolve_holdings(). Eventually org_units will have org_unit settings to hold
+their resolver type and base URL.
+
+=head1 AUTHOR
+
+Dan Scott, dscott at laurentian.ca
+
+=cut
+
+package OpenILS::Application::ResolverResolver;
+
+use strict;
+use warnings;
+use LWP::UserAgent;
+use XML::LibXML;
+
+# All OpenSRF applications must be based on OpenSRF::Application or
+# a subclass thereof.  Makes sense, eh?
+use OpenILS::Application;
+use base qw/OpenILS::Application/;
+
+# This is the client class, used for connecting to open-ils.storage
+use OpenSRF::AppSession;
+
+# This is an extension of Error.pm that supplies some error types to throw
+use OpenSRF::EX qw(:try);
+
+# This is a helper class for querying the OpenSRF Settings application ...
+use OpenSRF::Utils::SettingsClient;
+
+# ... and here we have the built in logging helper ...
+use OpenSRF::Utils::Logger qw($logger);
+
+# ... and this manages cached results for us ...
+use OpenSRF::Utils::Cache;
+
+# ... and this gives us access to the Fieldmapper
+use OpenILS::Utils::Fieldmapper;
+
+my $prefix = "open-ils.resolver_"; # Prefix for caching values
+my $cache;
+my $cache_timeout;
+my $default_url_base;              # Default resolver location
+
+our ($ua, $parser);
+
+
+sub initialize {
+    $cache = OpenSRF::Utils::Cache->new('global');
+    my $sclient = OpenSRF::Utils::SettingsClient->new();
+    $cache_timeout = $sclient->config_value(
+        "apps", "open-ils.resolver", "app_settings", "cache_timeout" ) || 300;
+    $default_url_base = $sclient->config_value(
+        "apps", "open-ils.resolver", "app_settings", "default_url_base");
+}
+
+sub child_init {
+    # We need a User Agent to speak to the SFX beast
+    $ua = new LWP::UserAgent;
+    $ua->agent('SameOrigin/1.0');
+
+    # SFX returns XML to us; let us parse
+    $parser = new XML::LibXML;
+}
+
+sub resolve_holdings {
+    my $self = shift;
+    my $conn = shift;
+    my $id_type = shift;      # keep it simple for now, either 'issn' or 'isbn'
+    my $id_value = shift;     # the normalized ISSN or ISBN
+    my $url_base = shift || $default_url_base; 
+
+    # We'll use this in our cache key
+    my $method = $self->api_name;
+
+    # We might want to return raw JSON for speedier responses
+    my $format = 'fieldmapper';
+    if ($self->api_name =~ /raw$/) {
+        $format = 'raw';
+    }
+
+    # Big ugly SFX OpenURL request
+    my $url_args = '?url_ver=Z39.88-2004&url_ctx_fmt=infofi/fmt:kev:mtx:ctx&'
+        . 'ctx_enc=UTF-8&ctx_ver=Z39.88-2004&rfr_id=info:sid/conifer&'
+        . 'sfx.ignore_date_threshold=1&'
+        . 'sfx.response_type=multi_obj_detailed_xml&__service_type=getFullTxt';
+
+    if ($id_type eq 'issn') {
+        $url_args .= "&rft.issn=$id_value";
+    } elsif ($id_type eq 'isbn') {
+        $url_args .= "&rft.isbn=$id_value";
+    }
+    
+    my $ckey = $prefix . $method . $url_base . $id_type . $id_value;
+
+    # Check the cache to see if we've already looked this up
+    # If we have, shortcut our return value
+    my $result = $cache->get_cache($ckey) || undef;
+    if ($result) {
+        $logger->info("Resolver found a cache hit");    
+        return $result;
+    }
+
+    # Otherwise, let's go and grab the info from the SFX server
+    my $req = HTTP::Request->new('GET', "$url_base$url_args");
+
+    # Let's see what we we're trying to request
+    $logger->info("Resolving the following request: $url_base$url_args");
+
+    my $res = $ua->request($req);
+
+    my $xml = $res->content;
+    my $parsed_sfx = $parser->parse_string($xml);
+
+    my (@targets) = $parsed_sfx->findnodes('//target');
+
+    my @sfx_result;
+    foreach my $target (@targets) {
+        if ($format eq 'raw') {
+            push @sfx_result, {
+                public_name => $target->findvalue('./target_public_name'),
+                target_url => $target->findvalue('.//target_url'),
+                target_coverage => $target->findvalue('.//coverage_statement'),
+                target_embargo => $target->findvalue('.//embargo_statement'),
+            };
+        } else {
+            my $rhr = Fieldmapper::resolver::holdings_record->new;
+            $rhr->public_name($target->findvalue('./target_public_name'));
+            $rhr->target_url($target->findvalue('.//target_url'));
+            $rhr->target_coverage($target->findvalue('.//coverage_statement'));
+            $rhr->target_embargo($target->findvalue('.//embargo_statement'));
+            push @sfx_result, $rhr;
+        }
+    }
+
+    # Stuff this into the cache
+    $cache->put_cache($ckey, \@sfx_result, $cache_timeout);
+    
+    # Don't return the list unless it contains results
+    if (scalar(@sfx_result)) {
+        return \@sfx_result;
+    }
+
+    return undef;
+}
+
+__PACKAGE__->register_method(
+    method    => 'resolve_holdings',
+    api_name  => 'open-ils.resolver.resolve_holdings',
+    api_level => 1,
+    argc      => 3,
+    signature => {
+        desc     => <<"         DESC",
+Returns a list of "rhr" objects representing the full-text holdings for a given ISBN or ISSN
+         DESC
+        'params' => [ {
+                name => 'id_type',
+                desc => 'The type of identifier ("issn" or "isbn")',
+                type => 'string' 
+            }, {
+                name => 'id_value',
+                desc => 'The identifier value',
+                type => 'string'
+            }, {
+                 name => 'url_base',
+                 desc => 'The base URL for the resolver and instance',
+                 type => 'string'
+            },
+        ],
+        'return' => {
+            desc => 'Returns a list of "rhr" objects representing the full-text holdings for a given ISBN or ISSN',
+            type => 'array'
+        }
+    }
+);
+
+__PACKAGE__->register_method(
+    method    => 'resolve_holdings',
+    api_name  => 'open-ils.resolver.resolve_holdings.raw',
+    api_level => 1,
+    argc      => 3,
+    signature => {
+        desc     => <<"         DESC",
+Returns a list of raw JSON objects representing the full-text holdings for a given ISBN or ISSN
+         DESC
+        'params' => [ {
+                name => 'id_type',
+                desc => 'The type of identifier ("issn" or "isbn")',
+                type => 'string' 
+            }, {
+                name => 'id_value',
+                desc => 'The identifier value',
+                type => 'string'
+            }, {
+                 name => 'url_base',
+                 desc => 'The base URL for the resolver and instance',
+                 type => 'string'
+            },
+        ],
+        'return' => {
+            desc => 'Returns a list of raw JSON objects representing the full-text holdings for a given ISBN or ISSN',
+            type => 'array'
+        }
+    }
+);
+
+# Clear cache for specific lookups
+sub delete_cached_holdings {
+    my $self = shift;
+    my $conn = shift;
+    my $id_type = shift;      # keep it simple for now, either 'issn' or 'isbn'
+    my $id_value = shift;     # the normalized ISSN or ISBN
+    my $url_base = shift || $default_url_base; 
+    my @deleted_keys;
+
+    $logger->warn("Deleting value [$id_value]");
+    # We'll use this in our cache key
+    foreach my $method ('open-ils.resolver.resolve_holdings.raw', 'open-ils.resolver.resolve_holdings') {
+        my $ckey = $prefix . $method . $url_base . $id_type . $id_value;
+
+        $logger->warn("Deleted cache key [$ckey]");
+        my $result = $cache->delete_cache($ckey);
+
+        $logger->warn("Result of deleting cache key: [$result]");
+        push @deleted_keys, $result;
+    }
+
+    return \@deleted_keys;
+}
+
+__PACKAGE__->register_method(
+    method    => 'delete_holdings_cache',
+    api_name  => 'open-ils.resolver.delete_cached_holdings',
+    api_level => 1,
+    argc      => 3,
+    signature => {
+        desc     => <<"         DESC",
+Deletes the cached value of the full-text holdings for a given ISBN or ISSN
+         DESC
+        'params' => [ {
+                name => 'id_type',
+                desc => 'The type of identifier ("issn" or "isbn")',
+                type => 'string' 
+            }, {
+                name => 'id_value',
+                desc => 'The identifier value',
+                type => 'string'
+            }, {
+                 name => 'url_base',
+                 desc => 'The base URL for the resolver and instance',
+                 type => 'string'
+            },
+        ],
+        'return' => {
+            desc => 'Deletes the cached value of the full-text holdings for a given ISBN or ISSN',
+            type => 'array'
+        }
+    }
+);
+
+
+1;



More information about the open-ils-commits mailing list