X-Git-Url: http://git.indexdata.com/?p=irspy-moved-to-github.git;a=blobdiff_plain;f=lib%2FZOOM%2FPod.pm;h=a59d3b119b25fb661dd4245571dfa3b8c1444882;hp=9a72b07f637de22fbade229d240d802d98cd003f;hb=bcac6b011cc4e33e99383d434a62cb6a5f52ff56;hpb=8f6a855fc5a2077f0b087ae81d6430522323af7e diff --git a/lib/ZOOM/Pod.pm b/lib/ZOOM/Pod.pm index 9a72b07..a59d3b1 100644 --- a/lib/ZOOM/Pod.pm +++ b/lib/ZOOM/Pod.pm @@ -1,4 +1,4 @@ -# $Id: Pod.pm,v 1.3 2006-05-09 16:31:05 mike Exp $ +# $Id: Pod.pm,v 1.12 2006-06-07 10:43:22 mike Exp $ package ZOOM::Pod; @@ -7,6 +7,16 @@ use warnings; use ZOOM; +BEGIN { + # Just register the name + ZOOM::Log::mask_str("pod"); + ZOOM::Log::mask_str("pod_unhandled"); +} + +=head1 NAME + +ZOOM::Pod - Perl extension for handling pods of concurrent ZOOM connections + =head1 SYNOPSIS use ZOOM::Pod; @@ -20,34 +30,78 @@ use ZOOM; die "$pod->wait() failed with error $err" if $err; sub completed_search { - ($conn, $rs) = @_; + ($conn, undef, $rs) = @_; print $conn->option("host"), ": found ", $rs->size(), " records\n"; - $rs->record(0); # Queues a request for the record + $rs->records(0, 1, 0); # Queues a request for the record return 0; } sub got_record { - ($conn, $rs) = @_; + ($conn, undef, $rs) = @_; $rec = $rs->record(0); print $conn->option("host"), ": got $rec = '", $rec->render(), "'\n"; return 0; } +=head1 DESCRIPTION + +C provides an API that simplifies asynchronous programming +using ZOOM. A pod is a collection of asynchronous connections that +are run simultaneously to achieve broadcast searching and retrieval. +When a pod is created, a set of connections (or target-strings to +connect to) are specified. Thereafter, they are treated as a unit, +and methods for searching, option-setting, etc. that are invoked on +the pod are delegated to each of its members. + +The key method on a pod is C, which enters a loop accepting +and dispatching events occurring on any of the connections in the pod. +Unless interrupted,the loop runs until there are no more events left, +i.e. no searches are outstanding and no requested records have still +to be received. + +Event dispatching is done by means of callback functions, which can be +registered for each event. A registered callback is invoked whenever +a corresponding event occurs. A special callback can be nominated to +handle errors. + +=head1 METHODS + +=head2 new() + + $pod = new ZOOM::Pod($conn1, $conn2, $conn3); + $pod = new ZOOM::Pod("bagel.indexdata.com/gils", + "bagel.indexdata.com/marc"); + +Creates a new pod containing one or more connections. Each connection +may be specified either by an existing C object, +which I be asynchronous; or by a ZOOM target string, in which +case the pod module will make the connection object itself. + +Returns the new pod. + =cut -BEGIN { - # Just register the name - ZOOM::Log::mask_str("pod"); -} +# Functionality to be added: +# +# If the constructor's first argument is a number, then it is +# taken as a limit on the number of connections to handle at any +# one time. In this case, the pod initially multiplexes between +# the first I connections, and brings further connections +# into the active subset whenever already-active connections are +# closed. sub new { my $class = shift(); my(@conn) = @_; + die "$class with no connections" if @conn == 0; my @state; # Hashrefs with application state associated with connections foreach my $conn (@conn) { if (!ref $conn) { $conn = new ZOOM::Connection($conn, 0, async => 1); + # The $conn object is always made, even if no there's no + # server. Such errors are caught later, by the _check() + # call in wait(). } push @state, {}; } @@ -60,15 +114,95 @@ sub new { }, $class; } +=head2 option() + + $oldElemSet = $pod->option("elementSetName"); + $pod->option(elementSetName => "b"); + +Sets a specified option in all the connections in a pod. Returns the +old value that the option had in first of the connections in the pod: +be aware that this value was not necessarily shared by all the members +of the pod ... but that is true often enough to be useful. + +=cut + sub option { my $this = shift(); my($key, $value) = @_; + my $old = $this->{conn}->[0]->option($key); foreach my $conn (@{ $this->{conn} }) { $conn->option($key, $value); } + + return $old; } +=head2 callback() + + $pod->callback(ZOOM::Event::RECV_SEARCH, \&completed_search); + $pod->callback("exception", sub { print "never mind: $@\n"; return 0 } ); + +Registers a callback to be invoked by the pod when an event happens. +Callback functions are invoked by C (q.v.). + +When registering a callback, the first argument is an event-code - one +of those defined in the C enumeration - and the second is +a function reference, or equivalently an inline code-fragment. It is +acceptable to nominate the same function as the callback for multiple +events, by multiple invocations of C. + +When an event occurs during the execution of C, the relevant +callback function is called with four arguments: the connection that the +event happened on; a state hash-reference associated with the +connection; the result-set associated with the connection; and the +event-type (so that a single function that handles events of multiple +types can switch on the code where necessary). The callback function +can handle the event as it wishes, finishing up by returning an +integer. If this is zero, then C continues as normal; if it +is anything else, then that value is immediately returned from +C. + +So a simple event-handler might look like this: + + sub got_event { + ($conn, $state, $rs, $event) = @_; + print "event $event on connection ", $conn->option("host"), "\n"; + print "Found ", $rs->size(), " records\n" + if $event == ZOOM::Event::RECV_SEARCH; + return 0; + } + +In addition to the event-type callbacks discussed above, there is a +special callback, C<"exception">, which is invoked if an exception +occurs. This will nearly always be a ZOOM error, but this can be +tested using C<$exception-Eisa("ZOOM::Exception")>. This callback is +invoked with the same arguments as described above, except that +instead of the event-type, the fourth argument is a copy of the +exception, C<$@>. Exception-handling callbacks may of course re-throw +the exception using C. + +So a simple error-handler might look like this: + + sub got_error { + ($conn, $state, $rs, $exception) = @_; + if ($exception->isa("ZOOM::Exception")) { + print "Caught error $exception - continuing"; + return 0; + } + die $exception; + } + +The C<$state> argument is a reference to an initially empty hash, +which the application can use as it sees fit, to store its own +connection-relation information. For example, an application might +use C<$state-E{last}> to keep a record of which was the last record +retrieved from the associated connection. The pod module itself does +not use the state hash at all, and applications are also welcome to +ignore it if they do not need it. + +=cut + sub callback { my $this = shift(); my($event, $sub) = @_; @@ -80,6 +214,28 @@ sub callback { return $old; } +=head2 search_pqf() + + $pod->search_pqf("@attr 1=1003 wedel"); + +Submits the specified query to each of the connections in a pod, +delegating to the same-named method of the C class +and storing each result in a result-set object associated with the +connection that generated it. Returns no value: success or failure +must subsequently be detected by inspecting the events and exceptions +generated by Cing on the pod. + +B +An important simplifying assumption is that each connection can only +have one search active on it at a time: this allows the pod to +maintain the one-to-one mapping between connections and result-sets. +Submitting a new search on a connection before the old one has +completed will result in a total failure in the nature of causality, +and the spontaneous existence-failure of the universe. Do not do +this. + +=cut + sub search_pqf { my $this = shift(); my($pqf) = @_; @@ -89,6 +245,28 @@ sub search_pqf { } } +=head2 wait() + + $err = $pod->wait(); + die "$pod->wait() failed with error $err" if $err; + +Waits for events on the connections that make up the pod, usually +continuing until there are no more events left and then returning +zero. Whenever an event occurs, a callback function is dispatched as +described above; if +that function returns a non-zero value, then C terminates +immediately, whether or not any events remain, and returns that value. + +If an error occurs on one of the connection in the pod, then it is +normally thrown as a C. If, however, there is a +special C<"exception"> callback registered, then the exception object +is passed to this instead. As usual, the return value of the callback +indicates whether C should continue (return-value 0) or return +immediately (any other value). Exception-handling callbacks may of +course re-throw the exception. + +=cut + sub wait { my $this = shift(); my $res = 0; @@ -96,12 +274,27 @@ sub wait { while ((my $i = ZOOM::event($this->{conn})) != 0) { my $conn = $this->{conn}->[$i-1]; my $ev = $conn->last_event(); - ZOOM::Log::log("pod", "connection ", $i-1, ": ", ZOOM::event_str($ev)); + my $evstr = ZOOM::event_str($ev); + ZOOM::Log::log("pod", "connection ", $i-1, ": event $ev ($evstr)"); + + eval { + $conn->_check(); + }; if ($@) { + my $sub = $this->{callback}->{exception}; + die $@ if !defined $sub; + $res = &$sub($conn, $this->{state}->[$i-1], + $this->{rs}->[$i-1], $@); + last if $res != 0; + next; + } + my $sub = $this->{callback}->{$ev}; if (defined $sub) { $res = &$sub($conn, $this->{state}->[$i-1], $this->{rs}->[$i-1], $ev); last if $res != 0; + } else { + ZOOM::Log::log("pod_unhandled", "connection ", $i-1, ": unhandled event $ev ($evstr)"); } } @@ -109,4 +302,49 @@ sub wait { } +=head1 LOGGING + +This module generates logging messages using C, +which in turn relies on the YAZ logging facilities. It uses two +logging levels: + +=over 4 + +=item pod + +Logs all events. + +=item pod_unhandled + +Logs unhandled events, i.e. events of types for which no callback has +been registered. + +=back + +These logging levels can be turned on by setting the C +environment variable to C. + +=head1 SEE ALSO + +The underlying +C +module (part of the +C +distribution). + +=head1 AUTHOR + +Mike Taylor, Emike@indexdata.comE + +=head1 COPYRIGHT AND LICENCE + +Copyright (C) 2006 by Index Data. + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself, either Perl version 5.8.4 or, +at your option, any later version of Perl 5 you may have available. + +=cut + + 1;