1 # $Id: Pod.pm,v 1.21 2006-09-27 12:48:20 mike Exp $
11 # Just register the names: this doesn't turn the levels on
12 ZOOM::Log::mask_str("pod");
13 ZOOM::Log::mask_str("pod_unhandled");
18 ZOOM::Pod - Perl extension for handling pods of concurrent ZOOM connections
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");
30 die "$pod->wait() failed with error $err" if $err;
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
40 ($conn, undef, $rs) = @_;
41 $rec = $rs->record(0);
42 print $conn->option("host"), ": got $rec = '", $rec->render(), "'\n";
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.
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
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
71 $pod = new ZOOM::Pod($conn1, $conn2, $conn3);
72 $pod = new ZOOM::Pod("bagel.indexdata.com/gils",
73 "bagel.indexdata.com/marc");
75 Creates a new pod containing one or more connections. Each connection
76 may be specified either by an existing C<ZOOM::Connection> object,
77 which I<must> be asynchronous; or by a ZOOM target string, in which
78 case the pod module will make the connection object itself.
84 # Functionality to be added:
86 # If the constructor's first argument is a number, then it is
87 # taken as a limit on the number of connections to handle at any
88 # one time. In this case, the pod initially multiplexes between
89 # the first I<n> connections, and brings further connections
90 # into the active subset whenever already-active connections are
97 die "$class with no connections" if @conn == 0;
98 foreach my $conn (@conn) {
100 $conn = new ZOOM::Connection($conn, 0, async => 1);
101 # The $conn object is always made, even if no there's no
102 # server. Such errors are caught later, by the _check()
116 $oldElemSet = $pod->option("elementSetName");
117 $pod->option(elementSetName => "b");
119 Sets a specified option in all the connections in a pod. Returns the
120 old value that the option had in first of the connections in the pod:
121 be aware that this value was not necessarily shared by all the members
122 of the pod ... but that is true often enough to be useful.
128 my($key, $value) = @_;
130 my $old = $this->{conn}->[0]->option($key);
131 foreach my $conn (@{ $this->{conn} }) {
132 $conn->option($key, $value);
140 $pod->callback(ZOOM::Event::RECV_SEARCH, \&completed_search);
141 $pod->callback("exception", sub { print "never mind: $@\n"; return 0 } );
143 Registers a callback to be invoked by the pod when an event happens.
144 Callback functions are invoked by C<wait()> (q.v.).
146 When registering a callback, the first argument is an event-code - one
147 of those defined in the C<ZOOM::Event> enumeration - and the second is
148 a function reference, or equivalently an inline code-fragment. It is
149 acceptable to nominate the same function as the callback for multiple
150 events, by multiple invocations of C<callback()>.
152 When an event occurs during the execution of C<wait()>, the relevant
153 callback function is called with four arguments: the connection that the
154 event happened on; the argument that was passed into C<wait()>;
155 the result-set associated with the connection (if there is one); and the
156 event-type (so that a single function that handles events of multiple
157 types can switch on the code where necessary). The callback function
158 can handle the event as it wishes, finishing up by returning an
159 integer. If this is zero, then C<wait()> continues as normal; if it
160 is anything else, then that value is immediately returned from
163 So a simple event-handler might look like this:
166 ($conn, $arg, $rs, $event) = @_;
167 print "event $event on connection ", $conn->option("host"), "\n";
168 print "Found ", $rs->size(), " records\n"
169 if $event == ZOOM::Event::RECV_SEARCH;
173 In addition to the event-type callbacks discussed above, there is a
174 special callback, C<"exception">, which is invoked if an exception
175 occurs. This will nearly always be a ZOOM error, but this can be
176 tested using C<$exception-E<gt>isa("ZOOM::Exception")>. This callback is
177 invoked with the same arguments as described above, except that
178 instead of the event-type, the fourth argument is a copy of the
179 exception, C<$@>. Exception-handling callbacks may of course re-throw
180 the exception using C<die $exception>.
182 So a simple error-handler might look like this:
185 ($conn, $arg, $rs, $exception) = @_;
186 if ($exception->isa("ZOOM::Exception")) {
187 print "Caught error $exception - continuing";
193 The C<$arg> argument could be anything at all - it is whatever the
194 application code passed into C<wait()>. For example, it could be
195 a reference to a hash indexed by the host string of the connections to
196 yield some per-connection state information.
197 An application might use such information
198 to keep a record of which was the last record
199 retrieved from the associated connection.
205 my($event, $sub) = @_;
207 my $old = $this->{callback}->{$event};
208 $this->{callback}->{$event} = $sub;
213 =head2 remove_callbacks()
215 $pod->remove_callbacks();
217 Removes all registed callbacks from the pod. This is useful when the
218 pod has completed one operation and is about to start the next.
222 sub remove_callbacks {
224 $this->{callback} = {};
229 $pod->search_pqf("@attr 1=1003 wedel");
231 Submits the specified query to each of the connections in a pod,
232 delegating to the same-named method of the C<ZOOM::Connection> class
233 and storing each result in a result-set object associated with the
234 connection that generated it. Returns no value: success or failure
235 must subsequently be detected by inspecting the events and exceptions
236 generated by C<wait()>ing on the pod.
239 An important simplifying assumption is that each connection can only
240 have one search active on it at a time: this allows the pod to
241 maintain the one-to-one mapping between connections and result-sets.
242 Submitting a new search on a connection before the old one has
243 completed will result in a total failure in the nature of causality,
244 and the spontaneous existence-failure of the universe. Try to avoid
245 doing this too often.
253 foreach my $i (0..@{ $this->{conn} }-1) {
254 my $conn = $this->{conn}->[$i];
255 $this->{rs}->[$i] = $conn->search_pqf($pqf)
256 if !$conn->option("pod_omit");
264 $err = $pod->wait($arg);
265 die "$pod->wait() failed with error $err" if $err;
267 Waits for events on the connections that make up the pod, usually
268 continuing until there are no more events left and then returning
269 zero. Whenever an event occurs, a callback function is dispatched as
270 described above; if an argument was passed to C<wait()>, then that
271 same argument is also passed to each callback invocation. If
272 that function returns a non-zero value, then C<wait()> terminates
273 immediately, whether or not any events remain, and returns that value.
275 If an error occurs on one of the connection in the pod, then it is
276 normally thrown as a C<ZOOM::Exception>. If, however, there is a
277 special C<"exception"> callback registered, then the exception object
278 is passed to this instead. As usual, the return value of the callback
279 indicates whether C<wait()> should continue (return-value 0) or return
280 immediately (any other value). Exception-handling callbacks may of
281 course re-throw the exception.
283 Connections that have the C<pod_omit> option set are omitted from
284 consideration. This is useful if, for example, a connection that is
285 part of a pod is known to have encountered an unrecoverable error.
297 my @idxmap; # maps indexes into conn to global indexes
298 foreach my $i (0 .. @{ $this->{conn} }-1) {
299 my $conn = $this->{conn}->[$i];
300 if ($conn->option("pod_omit")) {
301 #ZOOM::Log::log("pod", "connection $i omitted (",
302 #$conn->option("host"), ")");
306 #ZOOM::Log::log("pod", "connection $i included (",
307 #$conn->option("host"), ")");
312 my $i0 = ZOOM::event(\@conn);
314 my $i = 1+$idxmap[$i0-1];
315 my $conn = $this->{conn}->[$i-1];
316 die "connection-mapping screwup" if $conn ne $conn[$i0-1];
318 my $ev = $conn->last_event();
319 my $evstr = ZOOM::event_str($ev);
320 ZOOM::Log::log("pod", "connection ", $i-1, ": event $ev ($evstr)");
325 my $sub = $this->{callback}->{exception};
326 die $@ if !defined $sub;
327 $res = &$sub($conn, $arg, $this->{rs}->[$i-1], $@);
332 my $sub = $this->{callback}->{$ev};
334 $res = &$sub($conn, $arg, $this->{rs}->[$i-1], $ev);
337 ZOOM::Log::log("pod_unhandled", "connection ", $i-1, ": unhandled event $ev ($evstr)");
347 This module generates logging messages using C<ZOOM::Log::log()>,
348 which in turn relies on the YAZ logging facilities. It uses two
359 Logs unhandled events, i.e. events of types for which no callback has
364 These logging levels can be turned on by setting the C<YAZ_LOG>
365 environment variable to C<pod,pod_unhandled>.
377 Mike Taylor, E<lt>mike@indexdata.comE<gt>
379 =head1 COPYRIGHT AND LICENCE
381 Copyright (C) 2006 by Index Data.
383 This library is free software; you can redistribute it and/or modify
384 it under the same terms as Perl itself, either Perl version 5.8.4 or,
385 at your option, any later version of Perl 5 you may have available.