Partial documentation.
[irspy-moved-to-github.git] / lib / ZOOM / Pod.pm
1 # $Id: Pod.pm,v 1.6 2006-05-10 16:44:57 mike Exp $
2
3 package ZOOM::Pod;
4
5 use strict;
6 use warnings;
7
8 use ZOOM;
9
10 BEGIN {
11     # Just register the name
12     ZOOM::Log::mask_str("pod");
13     ZOOM::Log::mask_str("pod_unhandled");
14 }
15
16 =head1 NAME
17
18 ZOOM::Pod - Perl extension for handling pods of concurrent ZOOM connections
19
20 =head1 SYNOPSIS
21
22  use ZOOM::Pod;
23
24  $pod = new ZOOM::Pod("bagel.indexdata.com/gils",
25                       "bagel.indexdata.com/marc");
26  $pod->callback(ZOOM::Event::RECV_SEARCH, \&completed_search);
27  $pod->callback(ZOOM::Event::RECV_RECORD, \&got_record);
28  $pod->search_pqf("the");
29  $err = $pod->wait();
30  die "$pod->wait() failed with error $err" if $err;
31
32  sub completed_search {
33      ($conn, undef, $rs) = @_;
34      print $conn->option("host"), ": found ", $rs->size(), " records\n";
35      $rs->records(0, 1, 0); # Queues a request for the record
36      return 0;
37  }
38
39  sub got_record {
40      ($conn, undef, $rs) = @_;
41      $rec = $rs->record(0);
42      print $conn->option("host"), ": got $rec = '", $rec->render(), "'\n";
43      return 0;
44  }
45
46 =head1 DESCRIPTION
47
48 C<ZOOM:Pod> provides an API that simplifies asynchronous programming
49 using ZOOM.  A pod is a collection of asynchronous connections that
50 are run simultaneously to achieve broadcast searching and retrieval.
51 When a pod is created, a set of connections (or target-strings to
52 connect to) are specified.  Thereafter, they are treated as a unit,
53 and methods for searching, option-setting, etc. that are invoked on
54 the pod are delegated to each of its members.
55
56 The key method on a pod is C<wait()>, which enters a loop accepting
57 and dispatching events occurring on any of the connections in the pod.
58 Unless interrupted,the loop runs until there are no more events left,
59 i.e. no searches are outstanding and no requested records have still
60 to be received.
61
62 Event dispatching is done by means of callback functions, which can be
63 registered for each event.  A registered callback is invoked whenever
64 a corresponding event occurs.  A special callback can be nominated to
65 handle errors.
66
67 =head1 METHODS
68
69 =head2 new()
70
71  $pod = new ZOOM::Pod($conn1, $conn2, $conn3);
72  $pod = new ZOOM::Pod("bagel.indexdata.com/gils",
73                       "bagel.indexdata.com/marc");
74
75
76 Creates a new pod containing one or more connections.  Each connection
77 may be specified either by an existing C<ZOOM::Connection> object,
78 which I<must> be asynchronous; or by a ZOOM target string, in which
79 case the pod module will make the connection object itself.
80
81 Returns the new pod.
82
83 =cut
84
85 sub new {
86     my $class = shift();
87     my(@conn) = @_;
88
89     die "$class with no connections" if @conn == 0;
90     my @state; # Hashrefs with application state associated with connections
91     foreach my $conn (@conn) {
92         if (!ref $conn) {
93             $conn = new ZOOM::Connection($conn, 0, async => 1);
94             # The $conn object is always made, even if no there's no
95             # server.  Such errors are caught later, by the _check()
96             # call in wait(). 
97         }
98         push @state, {};
99     }
100
101     return bless {
102         conn => \@conn,
103         state => \@state,
104         rs => [],
105         callback => {},
106     }, $class;
107 }
108
109 =head2 option()
110
111  $oldElemSet = $pod->option("elementSetName");
112  $pod->option(elementSetName => "b");
113
114 Sets a specified option in all the connections in a pod.  Returns the
115 old value that the option had in first of the connections in the pod:
116 be aware that this value was not necessarily shared by all the members
117 of the pod ... but that is true often enough to be useful.
118
119 =cut
120
121 sub option {
122     my $this = shift();
123     my($key, $value) = @_;
124
125     my $old = $this->{conn}->[0]->option($key);
126     foreach my $conn (@{ $this->{conn} }) {
127         $conn->option($key, $value);
128     }
129
130     return $old;
131 }
132
133 =head2 callback()
134
135 I<###>
136
137 It is passed the connection that the event happened on, a state
138 hash-reference associated with the connection, the connection's
139 result-set and the event-type (so that a single function can handle
140 events of multiple types, switching on the code where necessary).
141
142 =cut
143
144 sub callback {
145     my $this = shift();
146     my($event, $sub) = @_;
147
148     my $old = $this->{callback}->{$event};
149     $this->{callback}->{$event} = $sub
150         if defined $sub;
151
152     return $old;
153 }
154
155 =head2 search_pqf()
156
157 I<###>
158
159 B<WARNING!>
160 An important simplifying assumption is that each connection can only
161 have one search active on it at a time - this allows the pod to
162 maintain a one-to-one mapping between connections and result-sets.  
163
164 =cut
165
166 sub search_pqf {
167     my $this = shift();
168     my($pqf) = @_;
169
170     foreach my $i (0..@{ $this->{conn} }-1) {
171         $this->{rs}->[$i] = $this->{conn}->[$i]->search_pqf($pqf);
172     }
173 }
174
175 =head2 wait()
176
177 I<###>
178
179 =cut
180
181 sub wait {
182     my $this = shift();
183     my $res = 0;
184
185     while ((my $i = ZOOM::event($this->{conn})) != 0) {
186         my $conn = $this->{conn}->[$i-1];
187         my $ev = $conn->last_event();
188         my $evstr = ZOOM::event_str($ev);
189         ZOOM::Log::log("pod", "connection ", $i-1, ": $evstr");
190
191         eval {
192             $conn->_check();
193         }; if ($@) {
194             my $sub = $this->{callback}->{exception};
195             die $@ if !defined $sub;
196             $res = &$sub($conn, $this->{state}->[$i-1],
197                          $this->{rs}->[$i-1], $@);
198             last if $res != 0;
199             next;
200         }
201
202         my $sub = $this->{callback}->{$ev};
203         if (defined $sub) {
204             $res = &$sub($conn, $this->{state}->[$i-1],
205                          $this->{rs}->[$i-1], $ev);
206             last if $res != 0;
207         } else {
208             ZOOM::Log::log("pod_unhandled", "unhandled event $ev ($evstr)");
209         }
210     }
211
212     return $res;
213 }
214
215
216 =head1 SEE ALSO
217
218 The underlying
219 C<ZOOM>
220 module (part of the
221 C<Net::Z3950::ZOOM>
222 distribution).
223
224 =head1 AUTHOR
225
226 Mike Taylor, E<lt>mike@indexdata.comE<gt>
227
228 =head1 COPYRIGHT AND LICENCE
229
230 Copyright (C) 2006 by Index Data.
231
232 This library is free software; you can redistribute it and/or modify
233 it under the same terms as Perl itself, either Perl version 5.8.4 or,
234 at your option, any later version of Perl 5 you may have available.
235
236 =cut
237
238
239 1;