Update copyright year
[ZOOM-Perl-moved-to-github.git] / lib / ZOOM.pod
index ee8162b..20fb9a1 100644 (file)
@@ -1,5 +1,3 @@
-# $Id: ZOOM.pod,v 1.24 2005-12-19 17:53:00 mike Exp $
-
 use strict;
 use warnings;
 
 use strict;
 use warnings;
 
@@ -11,7 +9,8 @@ ZOOM - Perl extension implementing the ZOOM API for Information Retrieval
 
  use ZOOM;
  eval {
 
  use ZOOM;
  eval {
-     $conn = new ZOOM::Connection($host, $port)
+     $conn = new ZOOM::Connection($host, $port,
+                                  databaseName => "mydb");
      $conn->option(preferredRecordSyntax => "usmarc");
      $rs = $conn->search_pqf('@attr 1=4 dinosaur');
      $n = $rs->size();
      $conn->option(preferredRecordSyntax => "usmarc");
      $rs = $conn->search_pqf('@attr 1=4 dinosaur');
      $n = $rs->size();
@@ -38,8 +37,8 @@ API such as ZOOM is that all implementations should be compatible
 anyway; but knowing that the same code is running is reassuring.)
 
 The ZOOM module provides two enumerations (C<ZOOM::Error> and
 anyway; but knowing that the same code is running is reassuring.)
 
 The ZOOM module provides two enumerations (C<ZOOM::Error> and
-C<ZOOM::Event>), two utility functions C<diag_str()> and C<event()> in
-the C<ZOOM> package itself, and eight classes:
+C<ZOOM::Event>), three utility functions C<diag_str()>, C<event_str()>
+and C<event()> in the C<ZOOM> package itself, and eight classes:
 C<ZOOM::Exception>,
 C<ZOOM::Options>,
 C<ZOOM::Connection>,
 C<ZOOM::Exception>,
 C<ZOOM::Options>,
 C<ZOOM::Connection>,
@@ -49,11 +48,16 @@ C<ZOOM::Record>,
 C<ZOOM::ScanSet>
 and
 C<ZOOM::Package>.
 C<ZOOM::ScanSet>
 and
 C<ZOOM::Package>.
-Of these, the Query class is abstract, and has two concrete
+Of these, the Query class is abstract, and has four concrete
 subclasses:
 subclasses:
-C<ZOOM::Query::CQL>
+C<ZOOM::Query::CQL>,
+C<ZOOM::Query::PQF>,
+C<ZOOM::Query::CQL2RPN>
 and
 and
-C<ZOOM::Query::PQF>.
+C<ZOOM::Query::CCL2RPN>.
+Finally, it also provides a
+C<ZOOM::Query::Log>
+module which supplies a useful general-purpose logging facility.
 Many useful ZOOM applications can be built using only the Connection,
 ResultSet, Record and Exception classes, as in the example
 code-snippet above.
 Many useful ZOOM applications can be built using only the Connection,
 ResultSet, Record and Exception classes, as in the example
 code-snippet above.
@@ -92,12 +96,27 @@ C<ZOOM::Connection::errcode()>,
 irrespective of whether it is a member of the C<ZOOM::Error>
 enumeration or drawn from the BIB-1 diagnostic set.
 
 irrespective of whether it is a member of the C<ZOOM::Error>
 enumeration or drawn from the BIB-1 diagnostic set.
 
-=head2 ZOOM::event()
+=head2 ZOOM::diag_srw_str()
 
 
-B<Warning.>
-Lark's vomit.  Do not read this section.
+ $msg = ZOOM::diag_srw_str(18);
+
+Returns a human-readable English-language string corresponding to the
+specified SRW error code.
+
+=head2 ZOOM::event_str()
 
 
- $which = ZOOM::event([ $conn1, $conn2, $conn3 ]);
+ $msg = ZOOM::event_str(ZOOM::Event::RECV_APDU);
+
+Returns a human-readable English-language string corresponding to the
+event code that is its own parameter.  This works for any value of the
+C<ZOOM::Event> enumeration.
+
+=head2 ZOOM::event()
+
+ $connsRef = [ $conn1, $conn2, $conn3 ];
+ $which = ZOOM::event($connsRef);
+ $ev = $connsRef->[$which-1]->last_event()
+     if ($which != 0);
 
 Used only in complex asynchronous applications, this function takes a
 reference to a list of Connection objects, waits until an event
 
 Used only in complex asynchronous applications, this function takes a
 reference to a list of Connection objects, waits until an event
@@ -106,8 +125,7 @@ the connections it occurred on.  The return value is a 1-based index
 into the list; 0 is returned if no event occurs within the longest
 timeout specified by the C<timeout> options of all the connections.
 
 into the list; 0 is returned if no event occurs within the longest
 timeout specified by the C<timeout> options of all the connections.
 
-B<Warning.>
-This function is not yet implemented.
+See the section below on asynchronous applications.
 
 =head1 CLASSES
 
 
 =head1 CLASSES
 
@@ -155,6 +173,9 @@ http://zoom.z3950.org/api/zoom-current.html#3.2
  $conn = new ZOOM::Connection("indexdata.dk:210/gils");
  $conn = new ZOOM::Connection("tcp:indexdata.dk:210/gils");
  $conn = new ZOOM::Connection("http:indexdata.dk:210/gils");
  $conn = new ZOOM::Connection("indexdata.dk:210/gils");
  $conn = new ZOOM::Connection("tcp:indexdata.dk:210/gils");
  $conn = new ZOOM::Connection("http:indexdata.dk:210/gils");
+ $conn = new ZOOM::Connection("indexdata.dk", 210,
+                               databaseName => "mydb",
+                               preferredRecordSyntax => "marc");
 
 Creates a new Connection object, and immediately connects it to the
 specified server.  If you want to make a new Connection object but
 
 Creates a new Connection object, and immediately connects it to the
 specified server.  If you want to make a new Connection object but
@@ -166,6 +187,16 @@ argument.  In the former case, the arguments are the name and port
 number of the Z39.50 server to connect to; in the latter case, the
 single argument is a YAZ service-specifier string of the form
 
 number of the Z39.50 server to connect to; in the latter case, the
 single argument is a YAZ service-specifier string of the form
 
+When the two-option form is used (which may be done using a vacuous
+second argument of zero), any number of additional argument pairs may
+be provided, which are interpreted as key-value pairs to be set as
+options after the Connection object is created but before it is
+connected to the server.  This is a convenient way to set options,
+including those that must be set before connecting such as
+authentication tokens.
+
+The server-name string is of the form:
+
 =over 4
 
 =item
 =over 4
 
 =item
@@ -199,11 +230,30 @@ local filesystem.
 
 =item http
 
 
 =item http
 
-SRW connection using SOAP over HTTP.
+SRU connection over HTTP.
 
 =back
 
 
 =back
 
-Support for SRU will follow in the fullness of time.
+If the C<http> scheme is used, the particular SRU flavour to be used
+may be specified by the C<sru> option, which takes the following
+values:
+
+=over 4
+
+=item soap
+
+SRU over SOAP (i.e. what used to be called SRW).
+This is the default.
+
+=item get
+
+"SRU Classic" (i.e. SRU over HTTP GET).
+
+=item post
+
+SRU over HTTP POST.
+
+=back
 
 If an error occurs, an exception is thrown.  This may indicate a
 networking problem (e.g. the host is not found or unreachable), or a
 
 If an error occurs, an exception is thrown.  This may indicate a
 networking problem (e.g. the host is not found or unreachable), or a
@@ -214,21 +264,28 @@ request).
 
  $options = new ZOOM::Options();
  $options->option(implementationName => "my client");
 
  $options = new ZOOM::Options();
  $options->option(implementationName => "my client");
+ $options->option(implementationId => 12345);
  $conn = create ZOOM::Connection($options)
  $conn = create ZOOM::Connection($options)
+ # or
+ $conn = create ZOOM::Connection(implementationName => "my client",
+                                 implementationId => 12345);
+
  $conn->connect($host, 0);
 
 The usual Connection constructor, C<new()> brings a new object into
 existence and forges the connection to the server all in one
 operation, which is often what you want.  For applications that need
  $conn->connect($host, 0);
 
 The usual Connection constructor, C<new()> brings a new object into
 existence and forges the connection to the server all in one
 operation, which is often what you want.  For applications that need
-more control, however, these two method separate the two steps,
+more control, however, these two methods separate the two steps,
 allowing additional steps in between such as the setting of options.
 
 C<create()> creates and returns a new Connection object, which is
 I<not> connected to any server.  It may be passed an options block, of
 type C<ZOOM::Options> (see below), into which options may be set
 allowing additional steps in between such as the setting of options.
 
 C<create()> creates and returns a new Connection object, which is
 I<not> connected to any server.  It may be passed an options block, of
 type C<ZOOM::Options> (see below), into which options may be set
-before or after the creation of the Connection.  The connection to the
-server may then be forged by the C<connect()> method, the arguments of
-which are the same as those of the C<new()> constructor.
+before or after the creation of the Connection.  Alternatively and
+equivalently, C<create()> may be passed a list of key-value option
+pairs directly.  The connection to the server may then be forged by
+the C<connect()> method, which accepts hostname and port arguments
+like those of the C<new()> constructor.
 
 =head4 error_x() / errcode() / errmsg() / addinfo() / diagset()
 
 
 =head4 error_x() / errcode() / errmsg() / addinfo() / diagset()
 
@@ -253,6 +310,24 @@ returns all four at once.
 
 See the C<ZOOM::Exception> for the interpretation of these elements.
 
 
 See the C<ZOOM::Exception> for the interpretation of these elements.
 
+=head4 exception()
+
+ die $conn->exception();
+
+C<exception()> returns the same information as C<error_x()> in the
+form of a C<ZOOM::Exception> object which may be thrown or rendered.
+If no error occurred on the connection, then C<exception()> returns an
+undefined value.
+
+=head4 check()
+
+ $conn->check();
+
+Checks whether an error is pending on the connection, and throw a
+C<ZOOM::Exception> object if so.  Since errors are thrown as they
+occur for synchronous connections, there is no need ever to call this
+except in asynchronous applications.
+
 =head4 option() / option_binary()
 
  print("server is '", $conn->option("serverImplementationName"), "'\n");
 =head4 option() / option_binary()
 
  print("server is '", $conn->option("serverImplementationName"), "'\n");
@@ -402,11 +477,8 @@ the C<ZOOM::Package> documentation.
 
 Returns a C<ZOOM::Event> enumerated value indicating the type of the
 last event that occurred on the connection.  This is used only in
 
 Returns a C<ZOOM::Event> enumerated value indicating the type of the
 last event that occurred on the connection.  This is used only in
-complex asynchronous applications - see the section below on
-C<ZOOM::Event> for more information.
-
-B<Warning.>
-This method has not been tested.
+complex asynchronous applications - see the sections below on the
+C<ZOOM::Event> enumeration and asynchronous applications.
 
 =head4 destroy()
 
 
 =head4 destroy()
 
@@ -471,7 +543,7 @@ Returns the number of records in the result set.
 The C<record()> method returns a C<ZOOM::Record> object representing
 a record from result-set, whose position is indicated by the argument
 passed in.  This is a zero-based index, so that legitimate values
 The C<record()> method returns a C<ZOOM::Record> object representing
 a record from result-set, whose position is indicated by the argument
 passed in.  This is a zero-based index, so that legitimate values
-range from zero to C<$rs->size()-1>.
+range from zero to C<$rs-E<gt>size()-1>.
 
 The C<record_immediate()> API is identical, but it never invokes a
 network operation, merely returning the record from the ResultSet's
 
 The C<record_immediate()> API is identical, but it never invokes a
 network operation, merely returning the record from the ResultSet's
@@ -497,7 +569,7 @@ wants to display a whole list of results.  Conversely, the software's
 strategy might be always to ask for blocks of a twenty records:
 that's great for assembling long lists of things, but wasteful when
 only one record is wanted.  The problem is that the ZOOM module can't
 strategy might be always to ask for blocks of a twenty records:
 that's great for assembling long lists of things, but wasteful when
 only one record is wanted.  The problem is that the ZOOM module can't
-tell, when you call C<$rs->record()>, what your intention is.
+tell, when you call C<$rs-E<gt>record()>, what your intention is.
 
 But you can tell it.  The C<records()> method fetches a sequence of
 records, all in one go.  It takes three arguments: the first is the
 
 But you can tell it.  The C<records()> method fetches a sequence of
 records, all in one go.  It takes three arguments: the first is the
@@ -587,21 +659,54 @@ http://zoom.z3950.org/api/zoom-current.html#3.5
 
 =head3 Methods
 
 
 =head3 Methods
 
+=head4 error() / exception()
+
+ if ($rec->error()) {
+     my($code, $msg, $addinfo, $dset) = $rec->error();
+     print "error $code, $msg ($addinfo) from $dset set\n";
+     die $rec->exception();
+ }
+
+These functions test for surrogate diagnostics associated with a
+record: that is, errors pertaining to a particular record rather than
+to the fetch-some-records operation as a whole.  (The latter are known
+in Z39.50 as non-surrogate diagnostics, and are reported as exceptions
+thrown by searches.)  If a particular record can't be obtained - for
+example, because it is not available in the requested record syntax -
+then the record object obtained from the result-set, when interrogated
+with these functions, will report the error.
+
+C<error()> returns the error-code, a human-readable message,
+additional information and the name of the diagnostic set that the
+error is from.  When called in a scalar context, it just returns the
+error-code.  Since error 0 means "no error", it can be used as a
+boolean has-there-been-an-error indicator.
+
+C<exception()> returns the same information in the form of a
+C<ZOOM::Exception> object which may be thrown or rendered.  If no
+error occurred on the record, then C<exception()> returns an undefined
+value.
+
 =head4 render()
 
 =head4 render()
 
- print $rec->render()
+ print $rec->render();
+ print $rec->render("charset=latin1,utf8");
 
 Returns a human-readable representation of the record.  Beyond that,
 no promises are made: careful programs should not make assumptions
 about the format of the returned string.
 
 
 Returns a human-readable representation of the record.  Beyond that,
 no promises are made: careful programs should not make assumptions
 about the format of the returned string.
 
+If the optional argument is provided, then it is interpreted as in the
+C<get()> method (q.v.)
+
 This method is useful mostly for debugging.
 
 =head4 raw()
 
 This method is useful mostly for debugging.
 
 =head4 raw()
 
- use MARC::Record
+ use MARC::Record;
  $raw = $rec->raw();
  $marc = new_from_usmarc MARC::Record($raw);
  $raw = $rec->raw();
  $marc = new_from_usmarc MARC::Record($raw);
+ $trans = $rec->render("charset=latin1,utf8");
 
 Returns an opaque blob of data that is the raw form of the record.
 Exactly what this is, and what you can do with it, varies depending on
 
 Returns an opaque blob of data that is the raw form of the record.
 Exactly what this is, and what you can do with it, varies depending on
@@ -611,6 +716,31 @@ that can be decoded by software such as the fine C<Marc::Record>
 module; GRS-1 record will be ... gosh, what an interesting question.
 But no-one uses GRS-1 any more, do they?
 
 module; GRS-1 record will be ... gosh, what an interesting question.
 But no-one uses GRS-1 any more, do they?
 
+If the optional argument is provided, then it is interpreted as in the
+C<get()> method (q.v.)
+
+=head4 get()
+
+ $raw = $rec->get("raw");
+ $rendered = $rec->get("render");
+ $trans = $rec->get("render;charset=latin1,utf8");
+ $trans = $rec->get("render", "charset=latin1,utf8");
+
+This is the underlying method used by C<render()> and C<raw()>, and
+which in turn delegates to the C<ZOOM_record_get()> function of the
+underlying ZOOM-C library.  Most applications will find it more
+natural to work with C<render()> and C<raw()>.
+
+C<get()> may be called with either one or two arguments.  The
+two-argument form is syntactic sugar: the two arguments are simply
+joined with a semi-colon to make a single argument, so the third and
+fourth example invocations above are equivalent.  The second argument
+(or portion of the first argument following the semicolon) is used in
+the C<type> argument of C<ZOOM_record_get()>, as described in
+http://www.indexdata.com/yaz/doc/zoom.records.tkl
+This is useful primarily for invoking the character-set transformation
+- in the examples above, from ISO Latin-1 to UTF-8 Unicode.
+
 =head4 clone() / destroy()
 
  $rec = $rs->record($i);
 =head4 clone() / destroy()
 
  $rec = $rs->record($i);
@@ -818,7 +948,7 @@ thrown if the server cannot execute the requested operations; and
 finally, destroy the package.
 
 Package options are listed at
 finally, destroy the package.
 
 Package options are listed at
-http://indexdata.com/yaz/doc/zoom.ext.html
+http://indexdata.com/yaz/doc/zoom.ext.tkl
 
 The particular options that have meaning are determined by the
 top-level operation string specified as the argument to C<send()>.
 
 The particular options that have meaning are determined by the
 top-level operation string specified as the argument to C<send()>.
@@ -862,7 +992,7 @@ http://indexdata.com/yaz/doc/zoom.ext.tkl
 
 =head4 send()
 
 
 =head4 send()
 
- $p->send("createdb");
+ $p->send("create");
 
 Sends a package to the server associated with the Connection that
 created it.  Problems are reported by throwing an exception.  The
 
 Sends a package to the server associated with the Connection that
 created it.  Problems are reported by throwing an exception.  The
@@ -934,7 +1064,7 @@ types of query.  The sole purpose of a Query object is to be used in a
 C<search()> on a Connection; because PQF is such a common special
 case, the shortcut Connection method C<search_pqf()> is provided.
 
 C<search()> on a Connection; because PQF is such a common special
 case, the shortcut Connection method C<search_pqf()> is provided.
 
-The following Query subclasses are provided, both of the providing the
+The following Query subclasses are provided, each providing the
 same set of methods described below:
 
 =over 4
 same set of methods described below:
 
 =over 4
@@ -957,6 +1087,34 @@ http://www.loc.gov/standards/sru/cql/
 and in a slight out-of-date but nevertheless useful tutorial at
 http://zing.z3950.org/cql/intro.html
 
 and in a slight out-of-date but nevertheless useful tutorial at
 http://zing.z3950.org/cql/intro.html
 
+=item ZOOM::Query::CQL2RPN
+
+Implements CQL by compiling it on the client-side into a Z39.50
+Type-1 (RPN) query, and sending that.  This provides essentially the
+same functionality as C<ZOOM::Query::CQL>, but it will work against
+any standard Z39.50 server rather than only against the small subset
+that support CQL natively.  The drawback is that, because the
+compilation is done on the client side, a configuration file is
+required to direct the mapping of CQL constructs such as index names,
+relations and modifiers into Type-1 query attributes.  An example CQL
+configuration file is included in the ZOOM-Perl distribution, in the
+file C<samples/cql/pqf.properties>
+
+=item ZOOM::Query::CCL2RPN
+
+Implements CCL by compiling it on the client-side into a Z39.50 Type-1
+(RPN) query, and sending that.  Because the compilation is done on the
+client side, a configuration file is required to direct the mapping of
+CCL constructs such as index names and boolean operators into Type-1
+query attributes.  An example CCL configuration file is included in
+the ZOOM-Perl distribution, in the file C<samples/ccl/default.bib>
+
+CCL is syntactically very similar to CQL, but much looser.  While CQL
+is an entirely precise language in which each possible query has
+rigorously defined semantics, and is thus suitable for transfer as
+part of a protocol, CCL is best deployed as a human-facing UI
+language.
+
 =back
 
 See the description of the C<Query> class in the ZOOM Abstract
 =back
 
 See the description of the C<Query> class in the ZOOM Abstract
@@ -967,14 +1125,36 @@ http://zoom.z3950.org/api/zoom-current.html#3.3
 
 =head4 new()
 
 
 =head4 new()
 
- $q = new ZOOM::Query::CQL('title=dinosaur'));
- $q = new ZOOM::Query::PQF('@attr 1=4 dinosaur'));
+ $q = new ZOOM::Query::CQL('title=dinosaur');
+ $q = new ZOOM::Query::PQF('@attr 1=4 dinosaur');
 
 Creates a new query object, compiling the query passed as its argument
 according to the rules of the particular query-type being
 instantiated.  If compilation fails, an exception is thrown.
 Otherwise, the query may be passed to the C<Connection> method
 
 Creates a new query object, compiling the query passed as its argument
 according to the rules of the particular query-type being
 instantiated.  If compilation fails, an exception is thrown.
 Otherwise, the query may be passed to the C<Connection> method
-<search()>.
+C<search()>.
+
+ $conn->option(cqlfile => "samples/cql/pqf.properties");
+ $q = new ZOOM::Query::CQL2RPN('title=dinosaur', $conn);
+
+Note that for the C<ZOOM::Query::CQL2RPN> subclass, the Connection
+must also be passed into the constructor.  This is used for two
+purposes: first, its C<cqlfile> option is used to find the CQL
+configuration file that directs the translations into RPN; and second,
+if compilation fails, then diagnostic information is cached in the
+Connection and be retrieved using C<$conn-E<gt>errcode()> and related
+methods.
+
+ $conn->option(cclfile => "samples/ccl/default.bib");
+ # or
+ $conn->option(cclqual => "ti u=4 s=pw\nab u=62 s=pw");
+ $q = new ZOOM::Query::CCL2RPN('ti=dinosaur', $conn);
+
+For the C<ZOOM::Query::CCL2RPN> subclass, too, the Connection must be
+passed into the constructor, for the same reasons as when client-side
+CQL compilation is used.  The C<cclqual> option, if defined, gives a
+CCL qualification specification inline; otherwise, the contents of the
+file named by the C<cclfile> option are used.
 
 =head4 sortby()
 
 
 =head4 sortby()
 
@@ -985,9 +1165,6 @@ is run on the query, the result is automatically sorted.  The sort
 specification language is the same as the C<yaz> sort-specification
 type of the C<ResultSet> method C<sort()>, described above.
 
 specification language is the same as the C<yaz> sort-specification
 type of the C<ResultSet> method C<sort()>, described above.
 
-B<It ought to be possible to sort by CQL query, too, but at present
-limitations in the underlying ZOOM-C library make this impossible.>
-
 =head4 destroy()
 
  $p->destroy()
 =head4 destroy()
 
  $p->destroy()
@@ -1149,20 +1326,25 @@ C<MEMORY>,
 C<ENCODE>,
 C<DECODE>,
 C<CONNECTION_LOST>,
 C<ENCODE>,
 C<DECODE>,
 C<CONNECTION_LOST>,
-C<INIT>,
+C<ZINIT>,
 C<INTERNAL>,
 C<TIMEOUT>,
 C<UNSUPPORTED_PROTOCOL>,
 C<UNSUPPORTED_QUERY>,
 C<INVALID_QUERY>,
 C<INTERNAL>,
 C<TIMEOUT>,
 C<UNSUPPORTED_PROTOCOL>,
 C<UNSUPPORTED_QUERY>,
 C<INVALID_QUERY>,
+C<CQL_PARSE>,
+C<CQL_TRANSFORM>,
+C<CCL_CONFIG>,
+C<CCL_PARSE>,
 C<CREATE_QUERY>,
 C<QUERY_CQL>,
 C<QUERY_PQF>,
 C<SORTBY>,
 C<CLONE>,
 C<CREATE_QUERY>,
 C<QUERY_CQL>,
 C<QUERY_PQF>,
 C<SORTBY>,
 C<CLONE>,
-C<PACKAGE>
+C<PACKAGE>,
+C<SCANTERM>
 and
 and
-C<SCANTERM>,
+C<LOGLEVEL>,
 each of which specifies a client-side error.  These codes constitute
 the C<ZOOM> diagnostic set.
 
 each of which specifies a client-side error.  These codes constitute
 the C<ZOOM> diagnostic set.
 
@@ -1187,11 +1369,179 @@ applications - The C<ZOOM::Connection::last_event()> method is used to
 return an indication of the last event that occurred on a particular
 connection.  It always returns a value drawn from this enumeration,
 that is, one of C<NONE>, C<CONNECT>, C<SEND_DATA>, C<RECV_DATA>,
 return an indication of the last event that occurred on a particular
 connection.  It always returns a value drawn from this enumeration,
 that is, one of C<NONE>, C<CONNECT>, C<SEND_DATA>, C<RECV_DATA>,
-C<TIMEOUT>, C<UNKNOWN>, C<SEND_APDU>, C<RECV_APDU>, C<RECV_RECORD> or
-C<RECV_SEARCH>.
+C<TIMEOUT>, C<UNKNOWN>, C<SEND_APDU>, C<RECV_APDU>, C<RECV_RECORD>,
+C<RECV_SEARCH> or C<ZEND>.
+
+See the section below on asynchronous applications.
 
 
-You almost certainly don't need to know about this.  Frankly, I'm not
-sure how to use it myself.
+=head1 LOGGING
+
+ ZOOM::Log::init_level(ZOOM::Log::mask_str("zoom,myapp,-warn"));
+ ZOOM::Log::log("myapp", "starting up with pid ", $$);
+
+Logging facilities are provided by a set of functions in the
+C<ZOOM::Log> module.  Note that C<ZOOM::Log> is not a class, and it
+is not possible to create C<ZOOM::Log> objects: the API is imperative,
+reflecting that of the underlying YAZ logging facilities.  Although
+there are nine logging functions altogether, you can ignore nearly
+all of them: most applications that use logging will begin by calling
+C<mask_str()> and C<init_level()> once each, as above, and will then
+repeatedly call C<log()>.
+
+=head2 mask_str()
+
+ $level = ZOOM::Log::mask_str("zoom,myapp,-warn");
+
+Returns an integer corresponding to the log-level specified by the
+parameter.  This is a string of zero or more comma-separated
+module-names, each indicating an individual module to be either added
+to the default log-level or removed from it (for those components
+prefixed by a minus-sign).  The names may be those of either standard
+YAZ-logging modules such as C<fatal>, C<debug> and C<warn>, or custom
+modules such as C<myapp> in the example above.  The module C<zoom>
+requests logging from the ZOOM module itself, which may be helpful for
+debugging.
+
+Note that calling this function does not in any way change the logging
+state: it merely returns a value.  To change the state, this value
+must be passed to C<init_level()>.
+
+=head2 module_level()
+
+ $level = ZOOM::Log::module_level("zoom");
+ ZOOM::Log::log($level, "all systems clear: thrusters invogriated");
+
+Returns the integer corresponding to the single log-level specified as
+the parameter, or zero if that level has not been registered by a
+prior call to C<mask_str()>.  Since C<log()> accepts either a numeric
+log-level or a string, there is no reason to call this function; but,
+what the heck, maybe you enjoy that kind of thing.  Who are we to
+judge?
+
+=head2 init_level()
+
+ ZOOM::Log::init_level($level);
+
+Initialises the log-level to the specified integer, which is a bitmask
+of values, typically as returned from C<mask_str()>.  All subsequent
+calls to C<log()> made with a log-level that matches one of the bits
+in this mask will result in a log-message being emitted.  All logging
+can be turned off by calling C<init_level(0)>.
+
+=head2 init_prefix()
+
+ ZOOM::Log::init_prefix($0);
+
+Initialises a prefix string to be included in all log-messages.
+
+=head2 init_file()
+
+ ZOOM::Log::init_file("/tmp/myapp.log");
+
+Initialises the output file to be used for logging: subsequent
+log-messages are written to the nominated file.  If this function is
+not called, log-messages are written to the standard error stream.
+
+=head2 init()
+
+ ZOOM::Log::init($level, $0, "/tmp/myapp.log");
+
+Initialises the log-level, the logging prefix and the logging output
+file in a single operation.
+
+=head2 time_format()
+
+ ZOOM::Log::time_format("%Y-%m-%d %H:%M:%S");
+
+Sets the format in which log-messages' timestamps are emitted, by
+means of a format-string like that used in the C function
+C<strftime()>.  The example above emits year, month, day, hours,
+minutes and seconds in big-endian order, such that timestamps can be
+sorted lexicographically.
+
+=head2 init_max_size()
+
+(This doesn't seem to work, so I won't bother describing it.)
+
+=head2 log()
+
+ ZOOM::Log::log(8192, "reducing to warp-factor $wf");
+ ZOOM::Log::log("myapp", "starting up with pid ", $$);
+
+Provided that the first argument, log-level, is among the modules
+previously established by C<init_level()>, this function emits a
+log-message made up of a timestamp, the prefix supplied to
+C<init_prefix()>, if any, and the concatenation of all arguments after
+the first.  The message is written to the standard output stream, or
+to the file previous specified by C<init_file()> if this has been
+called.
+
+The log-level argument may be either a numeric value, as returned from
+C<module_level()>, or a string containing the module name.
+
+=head1 ASYNCHRONOUS APPLICATIONS
+
+Although asynchronous applications are conceptually complex, the ZOOM
+support for them is provided through a very simple interface,
+consisting of one option (C<async>), one function (C<ZOOM::event()>),
+one Connection method (C<last_event()> and an enumeration
+(C<ZOOM::Event>).
+
+The approach is as follows:
+
+=over 4
+
+=item Initialisation
+
+Create several connections to the various servers, each of them having
+the option C<async> set, and with whatever additional options are
+required - e.g. the piggyback retrieval record-count can be set so
+that records will be returned in search responses.
+
+=item Operations
+
+Send searches to the connections, request records, etc.
+
+=item Event harvesting
+
+Repeatedly call C<ZOOM::event()> to discover what responses are being
+received from the servers.  Each time this function returns, it
+indicates which of the connections has fired; this connection can then
+be interrogated with the C<last_event()> method to discover what event
+has occurred, and the return value - an element of the C<ZOOM::Event>
+enumeration - can be tested to determine what to do next.  For
+example, the C<ZEND> event indicates that no further operations are
+outstanding on the connection, so any fetched records can now be
+immediately obtained.
+
+=back
+
+Here is a very short program (omitting all error-checking!) which
+demonstrates this process.  It parallel-searches three servers (or more
+of you add them the list), displaying the first record in the
+result-set of each server as soon as it becomes available.
+
+ use ZOOM;
+ @servers = ('z3950.loc.gov:7090/Voyager',
+             'z3950.indexdata.com:210/gils',
+             'agricola.nal.usda.gov:7190/Voyager');
+ for ($i = 0; $i < @servers; $i++) {
+     $z[$i] = new ZOOM::Connection($servers[$i], 0,
+                                   async => 1, # asynchronous mode
+                                   count => 1, # piggyback retrieval count
+                                   preferredRecordSyntax => "usmarc");
+     $r[$i] = $z[$i]->search_pqf("mineral");
+ }
+ while (($i = ZOOM::event(\@z)) != 0) {
+     $ev = $z[$i-1]->last_event();
+     print("connection ", $i-1, ": ", ZOOM::event_str($ev), "\n");
+     if ($ev == ZOOM::Event::ZEND) {
+         $size = $r[$i-1]->size();
+         print "connection ", $i-1, ": $size hits\n";
+         print $r[$i-1]->record(0)->render()
+             if $size > 0;
+     }
+ }
 
 =head1 SEE ALSO
 
 
 =head1 SEE ALSO
 
@@ -1216,7 +1566,7 @@ Mike Taylor, E<lt>mike@indexdata.comE<gt>
 
 =head1 COPYRIGHT AND LICENCE
 
 
 =head1 COPYRIGHT AND LICENCE
 
-Copyright (C) 2005 by Index Data.
+Copyright (C) 2005-2014 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,
 
 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,