<chapter id="zoom">
- <!-- $Id: zoom.xml,v 1.3 2002-10-09 09:07:10 mike Exp $ -->
+ <!-- $Id: zoom.xml,v 1.17 2006-10-10 21:59:43 adam Exp $ -->
<title>ZOOM-C++</title>
- <sect1 id="zoom.introduction">
+
+ <sect1 id="zoom-introduction">
<title>Introduction</title>
<para>
- <ulink url="http://staging.zoom.z3950.org/">ZOOM</ulink>
+ <ulink url="&url.zoom;">ZOOM</ulink>
is the emerging standard API for information retrieval programming
using the Z39.50 protocol. ZOOM's
- <ulink url="http://staging.zoom.z3950.org/api/">Abstract API</ulink>
+ <ulink url="&url.zoom.api;">Abstract API</ulink>
specifies semantics for classes representing key IR concepts such as
connections, queries, result sets and records; and there are various
- <ulink url="http://staging.zoom.z3950.org/bind/">bindings</ulink>
+ <ulink url="&url.zoom.bind;">bindings</ulink>
specifying how those concepts should be represented in various
programming languages.
</para>
<para>
- The Yaz++ library includes an implementation of the <ulink
- url="http://staging.zoom.z3950.org/bind/cplusplus/"
- >C++ binding</ulink>
+ The YAZ++ library includes an implementation of the <ulink
+ url="&url.zoom.bind.cplusplus;">C++ binding</ulink>
for ZOOM, enabling quick, easy development of client applications.
</para>
<para>
For example, here is a tiny Z39.50 client that fetches and displays
- the MARC record for Farlow & Brett Surman's
- <!-- ### there must be a better way to mark up a book title? -->
- <emphasis>The Complete Dinosaur</emphasis>
+ the MARC record for Farlow & Brett Surman's
+ <citetitle>The Complete Dinosaur</citetitle>
from the Library of Congress's Z39.50 server:
- <screen>
+ </para>
+ <programlisting>
#include <iostream>
- #include <yaz++/zoom.h>
+ #include <yazpp/zoom.h>
using namespace ZOOM;
{
connection conn("z3950.loc.gov", 7090);
conn.option("databaseName", "Voyager");
- resultSet rs(conn, prefixQuery("@attr attr 1=7 0253333490"));
+ conn.option("preferredRecordSyntax", "USMARC");
+ resultSet rs(conn, prefixQuery("@attr 1=7 0253333490"));
const record *rec = rs.getRecord(0);
cout << rec->render() << endl;
}
- </screen>
- (Note that, for the sake of simplicity, this does not check for
- errors: we show a more realistic version of this program later.)
- </para>
+ </programlisting>
+ <note>
+ <para>
+ For the sake of simplicity, this program does not check
+ for errors: we show a more robust version of the same program
+ <link linkend="revised-sample">later</link>.)
+ </para>
+ </note>
<para>
- Yaz++'s implementation of the C++ binding is a thin layer over Yaz's
+ YAZ++'s implementation of the C++ binding is a thin layer over YAZ's
implementation of the C binding. For information on the supported
options and other such details, see the ZOOM-C documentation, which
can be found on-line at
- <ulink url="http://www.indexdata.dk/yaz/doc/zoom.php"/>
+ <ulink url="&url.yaz.zoom;"/>
</para>
<para>
All of the classes defined by ZOOM-C++ are in the
</para>
</sect1>
- <sect1>
+
+ <sect1 id="zoom-connection">
<title><literal>ZOOM::connection</literal></title>
<para>
- SEE ALSO
- <ulink url="http://staging.zoom.z3950.org/api/zoom-1.3.html#3.2"
- >Section 3.2 (Connection) of the ZOOM Abstract API</ulink>
- </para>
- <para>
A <literal>ZOOM::connection</literal> object represents an open
connection to a Z39.50 server. Such a connection is forged by
constructing a <literal>connection</literal> object.
</para>
<para>
The class has this declaration:
- <screen>
+ </para>
+ <synopsis>
class connection {
public:
connection (const char *hostname, int portnum);
const char *option (const char *key) const;
const char *option (const char *key, const char *val);
};
- </screen>
+ </synopsis>
+ <para>
+ When a new <literal>connection</literal> is created, the hostname
+ and port number of a Z39.50 server must be supplied, and the
+ network connection is forged and wrapped in the new object. If the
+ connection can't be established - perhaps because the hostname
+ couldn't be resolved, or there is no server listening on the
+ specified port - then an
+ <link linkend="zoom-exception"><literal>exception</literal></link>
+ is thrown.
</para>
<para>
- ### discusson
+ The only other methods on a <literal>connection</literal> object
+ are for getting and setting options. Any name-value pair of
+ strings may be set as options, and subsequently retrieved, but
+ certain options have special meanings which are understood by the
+ ZOOM code and affect the behaviour of the object that carries
+ them. For example, the value of the
+ <literal>databaseName</literal> option is used as the name of the
+ database to query when a search is executed against the
+ <literal>connection</literal>. For a full list of such special
+ options, see the ZOOM abstract API and the ZOOM-C documentation
+ (links below).
</para>
- </sect1>
- <sect1>
+ <sect2 id="connection.references">
+ <title>References</title>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <ulink url="http://zoom.z3950.org/api/zoom-1.3.html#3.2"
+ >Section 3.2 (Connection) of the ZOOM Abstract API</ulink>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <ulink url="&url.yaz.zoom.connections;"
+ >The Connections section f the ZOOM-C documentation</ulink>
+ </para>
+ </listitem>
+ </itemizedlist>
+ </sect2>
+ </sect1>
+
+
+ <sect1 id="zoom-query">
<title><literal>ZOOM::query</literal> and subclasses</title>
<para>
- SEE ALSO
- <ulink url="http://staging.zoom.z3950.org/api/zoom-1.3.html#3.3"
- >Section 3.3 (Query) of the ZOOM Abstract API</ulink>
- </para>
- <para>
The <literal>ZOOM::query</literal> class is a virtual base class,
representing a query to be submitted to a server. This class has
- no methods, but two (so far) concrete subclasses:
+ no methods, but two (so far) concrete subclasses, each implementing
+ a specific query notation.
</para>
- <sect2>
+ <sect2 id="ZOOM::prefixQuery">
<title><literal>ZOOM::prefixQuery</literal></title>
- <para>
- The class has this declaration:
- <screen>
+ <synopsis>
class prefixQuery : public query {
public:
prefixQuery (const char *pqn);
~prefixQuery ();
};
- </screen>
- </para>
- </sect2>
-
- <sect2>
+ </synopsis>
+ <para>
+ This class enables a query to be created by compiling YAZ's
+ cryptic but powerful
+ <ulink url="&url.yaz.pqf;">Prefix Query Notation (PQN)</ulink>.
+ </para>
+ </sect2>
+
+ <sect2 id="ZOOM::CCLQuery">
<title><literal>ZOOM::CCLQuery</literal></title>
- <para>
- The class has this declaration:
- <screen>
+ <synopsis>
class CCLQuery : public query {
public:
CCLQuery (const char *ccl, void *qualset);
~CCLQuery ();
};
- </screen>
+ </synopsis>
+ <para>
+ This class enables a query to be created using the simpler but
+ less expressive
+ <ulink url="&url.yaz.ccl;">Common Command Language (CCL)</ulink>.
+ The qualifiers recognised by the CCL parser are specified in an
+ external configuration file in the format described by the YAZ
+ documentation.
+ </para>
+ <para>
+ If query construction fails for either type of
+ <literal>query</literal> object - typically because the query
+ string itself is not valid PQN or CCL - then an
+ <link linkend="zoom-exception"><literal>exception</literal></link>
+ is thrown.
</para>
</sect2>
- <sect2>
+ <sect2 id="queries.discussion">
<title>Discussion</title>
<para>
It will be readily recognised that these objects have no methods
<literal>resultSet</literal> class's constructor.
</para>
<para>
- ### discusson
+ Given a suitable set of CCL qualifiers, the following pairs of
+ queries are equivalent:
</para>
+ <screen>
+ prefixQuery("dinosaur");
+ CCLQuery("dinosaur");
+
+ prefixQuery("@and complete dinosaur");
+ CCLQuery("complete and dinosaur");
+
+ prefixQuery("@and complete @or dinosaur pterosaur");
+ CCLQuery("complete and (dinosaur or pterosaur)");
+
+ prefixQuery("@attr 1=7 0253333490");
+ CCLQuery("isbn=0253333490");
+ </screen>
+ </sect2>
+
+ <sect2 id="query.references">
+ <title>References</title>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <ulink url="http://zoom.z3950.org/api/zoom-1.3.html#3.3"
+ >Section 3.3 (Query) of the ZOOM Abstract API</ulink>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <ulink url="&url.yaz.zoom.query;"
+ >The Queries section of the ZOOM-C documentation</ulink>
+ </para>
+ </listitem>
+ </itemizedlist>
</sect2>
</sect1>
- <sect1>
+
+ <sect1 id="zoom-resultset">
<title><literal>ZOOM::resultSet</literal></title>
<para>
- SEE ALSO
- <ulink url="http://staging.zoom.z3950.org/api/zoom-1.3.html#3.4"
- >Section 3.4 (Result Set) of the ZOOM Abstract API</ulink>
- </para>
- <para>
A <literal>ZOOM::resultSet</literal> object represents a set of
- record identified by a query that has been executed against a
- particular connection.
+ records identified by a query that has been executed against a
+ particular connection. The sole purpose of both
+ <literal>connection</literal> and <literal>query</literal> objects
+ is that they can be used to create new
+ <literal>resultSet</literal>s - that is, to perform a search on the
+ server on the remote end of the connection.
</para>
<para>
The class has this declaration:
- <screen>
+ </para>
+ <synopsis>
class resultSet {
public:
resultSet (connection &c, const query &q);
size_t size () const;
const record *getRecord (size_t i) const;
};
- </screen>
+ </synopsis>
+ <para>
+ New <literal>resultSet</literal>s are created by the constructor,
+ which is passed a <literal>connection</literal>, indicating the
+ server on which the search is to be performed, and a
+ <literal>query</literal>, indicating what search to perform. If
+ the search fails - for example, because the query uses attributes
+ that the server doesn't implement - then an
+ <link linkend="zoom-exception"><literal>exception</literal></link>
+ is thrown.
</para>
<para>
- ### discusson
+ Like <literal>connection</literal>s, <literal>resultSet</literal>
+ objects can carry name-value options. The special options which
+ affect ZOOM-C++'s behaviour are the same as those for ZOOM-C and
+ are described in its documentation (link below). In particular,
+ the <literal>preferredRecordSyntax</literal> option may be set to
+ a string such as ``USMARC'', ``SUTRS'' etc. to indicate what the
+ format in which records should be retrieved; and the
+ <literal>elementSetName</literal> option indicates whether brief
+ records (``B''), full records (``F'') or some other composition
+ should be used.
</para>
+ <para>
+ The <literal>size()</literal> method returns the number of records
+ in the result set. Zero is a legitimate value: a search that finds
+ no records is not the same as a search that fails.
+ </para>
+ <para>
+ Finally, the <literal>getRecord</literal> method returns the
+ <parameter>i</parameter>th record from the result set, where
+ <parameter>i</parameter> is zero-based: that is, legitmate values
+ range from zero up to one less than the result-set size. If the
+ method fails, for example because the requested record is out of
+ range, it <literal>throw</literal>s an
+ <link linkend="zoom-exception"><literal>exception</literal></link>.
+ </para>
+
+ <sect2 id="resultset.references">
+ <title>References</title>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <ulink url="http://zoom.z3950.org/api/zoom-1.3.html#3.4"
+ >Section 3.4 (Result Set) of the ZOOM Abstract API</ulink>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <ulink url="&url.yaz.zoom.resultsets;"
+ >The Result Sets section of the ZOOM-C documentation</ulink>
+ </para>
+ </listitem>
+ </itemizedlist>
+ </sect2>
</sect1>
- <sect1>
+
+ <sect1 id="zoom-record">
<title><literal>ZOOM::record</literal></title>
<para>
- SEE ALSO
- <ulink url="http://staging.zoom.z3950.org/api/zoom-1.3.html#3.5"
- >Section 3.5 (Record) of the ZOOM Abstract API</ulink>
- </para>
- <para>
A <literal>ZOOM::record</literal> object represents a chunk of data
from a <literal>resultSet</literal> returned from a server.
</para>
<para>
The class has this declaration:
- <screen>
+ </para>
+ <synopsis>
class record {
public:
~record ();
const char *render () const;
const char *rawdata () const;
};
- </screen>
+ </synopsis>
+ <para>
+ Records returned from Z39.50 servers are encoded using a record
+ syntax: the various national MARC formats are commonly used for
+ bibliographic data, GRS-1 or XML for complex structured data, SUTRS
+ for simple human-readable text, etc. The
+ <literal>record::syntax</literal> enumeration specifies constants
+ representing common record syntaxes, and the
+ <literal>recsyn()</literal> method returns the value corresponding
+ to the record-syntax of the record on which it is invoked.
+ <note>
+ <para>
+ Because this interface uses an enumeration, it is difficult to
+ extend to other record syntaxes - for example, DANMARC, the MARC
+ variant widely used in Denmark. We might either grow the
+ enumeration substantially, or change the interface to return
+ either an integer or a string.
+ </para>
+ </note>
+ </para>
+ <para>
+ The simplest thing to do with a retrieved record is simply to
+ <literal>render()</literal> it. This returns a human-readable, but
+ not necessarily very pretty, representation of the contents of the
+ record. This is useful primarily for testing and debugging, since
+ the application has no control over how the record appears.
+ (The application must <emphasis>not</emphasis>
+ <literal>delete</literal> the returned string - it is ``owned'' by
+ the record object.)
</para>
<para>
- ### discusson
+ More sophisticated applications will want to deal with the raw data
+ themselves: the <literal>rawdata()</literal> method returns it.
+ Its format will vary depending on the record syntax: SUTRS, MARC
+ and XML records are returned ``as is'', and GRS-1 records as a
+ pointer to their top-level node, which is a
+ <literal>Z_GenericRecord</literal> structure as defined in the
+ <literal><yaz/z-grs.h></literal> header file.
+ (The application must <emphasis>not</emphasis>
+ <literal>delete</literal> the returned data - it is ``owned'' by
+ the record object.)
</para>
+ <para>
+ Perceptive readers will notice that there are no methods for access
+ to individual fields within a record. That's because the different
+ record syntaxes are so different that there is no even a uniform
+ notion of what a field is across them all, let alone a sensible way
+ to implement such a function. Fetch the raw data instead, and pick
+ it apart ``by hand''.
+ </para>
+
+ <sect2 id="zoom.memory.management">
+ <title>Memory Management</title>
+ <para>
+ The <literal>record</literal> objects returned from
+ <literal>resultSet::getRecord()</literal> are ``owned'' by the
+ result set object: that means that the application is not
+ responsible for <literal>delete</literal>ing them - each
+ <literal>record</literal> is automatically deallocated when the
+ <literal>resultSet</literal> that owns it is
+ <literal>delete</literal>d.
+ </para>
+ <para>
+ Usually that's what you want: it means that you can easily fetch a
+ record, use it and forget all about it, like this:
+ </para>
+ <programlisting>
+ resultSet rs(conn, query);
+ cout << rs.getRecord(0)->render();
+ </programlisting>
+ <para>
+ But sometimes you want a <literal>record</literal> to live on past
+ the lifetime of the <literal>resultSet</literal> from which it was
+ fetched. In this case, the <literal>clone(f)</literal> method can
+ be used to make an autonomous copy. The application must
+ <literal>delete</literal> it when it doesn't need it any longer:
+ </para>
+ <programlisting>
+ record *rec;
+ {
+ resultSet rs(conn, query);
+ rec = rs.getRecord(0)->clone();
+ // `rs' goes out of scope here, and is deleted
+ }
+ cout << rec->render();
+ delete rec;
+ </programlisting>
+ </sect2>
+
+ <sect2 id="record.references">
+ <title>References</title>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <ulink url="http://zoom.z3950.org/api/zoom-1.3.html#3.5"
+ >Section 3.5 (Record) of the ZOOM Abstract API</ulink>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <ulink url="&url.yaz.zoom.records;"
+ >The Records section of the ZOOM-C documentation</ulink>
+ </para>
+ </listitem>
+ </itemizedlist>
+ </sect2>
</sect1>
- <sect1>
+
+ <sect1 id="zoom-exception">
<title><literal>ZOOM::exception</literal> and subclasses</title>
<para>
- SEE ALSO
- <ulink url="http://staging.zoom.z3950.org/api/zoom-1.3.html#3.7"
- >Section 3.7 (Exception) of the ZOOM Abstract API</ulink>
- </para>
- <para>
The <literal>ZOOM::exception</literal> class is a virtual base
class, representing a diagnostic generated by the ZOOM-C++ library
- or returned from a server. ###
- <screen>
+ or returned from a server. Its subclasses represent particular
+ kinds of error.
+ </para>
+ <para>
+ When any of the ZOOM methods fail, they respond by
+ <literal>throw</literal>ing an object of type
+ <literal>exception</literal> or one of its subclasses. This most
+ usually happens with the <literal>connection</literal> constructor,
+ the various query constructors, the <literal>resultSet</literal>
+ constructor (which is actually the searching method) and
+ <literal>resultSet::getRecord()</literal>.
+ </para>
+ <para>
+ The base class has this declaration:
+ </para>
+ <synopsis>
class exception {
public:
exception (int code);
int errcode () const;
const char *errmsg () const;
};
- </screen>
- This class has three (so far) concrete subclasses:
+ </synopsis>
+ <para>
+ It has three concrete subclasses:
</para>
- <sect2>
+ <sect2 id="ZOOM::systemException">
<title><literal>ZOOM::systemException</literal></title>
- <para>
- The class has this declaration:
- <screen>
+ <synopsis>
class systemException: public exception {
public:
systemException ();
int errcode () const;
const char *errmsg () const;
};
- </screen>
+ </synopsis>
+ <para>
+ Represents a ``system error'', typically indicating that a system
+ call failed - often in the low-level networking code that
+ underlies Z39.50. <literal>errcode()</literal> returns the value
+ that the system variable <literal>errno</literal> had at the time
+ the exception was constructed; and <literal>errmsg()</literal>
+ returns a human-readable error-message corresponidng to that error
+ code.
</para>
</sect2>
- <sect2>
+ <sect2 id="ZOOM::bib1Exception">
<title><literal>ZOOM::bib1Exception</literal></title>
- <para>
- The class has this declaration:
- <screen>
+ <synopsis>
class bib1Exception: public exception {
public:
bib1Exception (int errcode, const char *addinfo);
const char *errmsg () const;
const char *addinfo () const;
};
- </screen>
+ </synopsis>
+ <para>
+ Represents an error condition communicated by a Z39.50 server.
+ <literal>errcode()</literal> returns the BIB-1 diagnostic code of
+ the error, and <literal>errmsg()</literal> a human-readable error
+ message corresponding to that code. <literal>addinfo()</literal>
+ returns any additional information associated with the error.
+ </para>
+ <para>
+ For example, if a ZOOM application tries to search in the
+ ``Voyager'' database of a server that does not have a database of
+ that name, a <literal>bib1Exception</literal> will be thrown in
+ which <literal>errcode()</literal> returns 109,
+ <literal>errmsg()</literal> returns the corresponding error
+ message ``Database unavailable'' and <literal>addinfo()</literal>
+ returns the name of the requested, but unavailable, database.
</para>
</sect2>
- <sect2>
+ <sect2 id="ZOOM::queryException">
<title><literal>ZOOM::queryException</literal></title>
- <para>
- The class has this declaration:
- <screen>
+ <synopsis>
class queryException: public exception {
public:
static const int PREFIX = 1;
const char *errmsg () const;
const char *addinfo () const;
};
- </screen>
+ </synopsis>
+ <para>
+ This class represents an error in parsing a query into a form that
+ a Z39.50 can understand. It must be created with the
+ <literal>qtype</literal> parameter equal to one of the query-type
+ constants, which can be retrieved via the
+ <literal>errcode()</literal> method; <literal>errmsg()</literal>
+ returns an error-message specifying which kind of query was
+ malformed; and <literal>addinfo()</literal> returns a copy of the
+ query itself (that is, the value of <literal>source</literal> with
+ which the exception object was created.)
</para>
</sect2>
- <sect2>
- <title>Discussion</title>
+ <sect2 id="revised-sample">
+ <title>Revised Sample Program</title>
+ <para>
+ Now we can revise the sample program from the
+ <link linkend="zoom-introduction">introduction</link>
+ to catch exceptions and report any errors:
+ </para>
+ <programlisting>
+ /* g++ -o zoom-c++-hw zoom-c++-hw.cpp -lzoompp -lyaz */
+
+ #include <iostream>
+ #include <yazpp/zoom.h>
+
+ using namespace ZOOM;
+
+ int main(int argc, char **argv)
+ {
+ try {
+ connection conn("z3950.loc.gov", 7090);
+ conn.option("databaseName", "Voyager");
+ conn.option("preferredRecordSyntax", "USMARC");
+ resultSet rs(conn, prefixQuery("@attr 1=7 0253333490"));
+ const record *rec = rs.getRecord(0);
+ cout << rec->render() << endl;
+ } catch (systemException &e) {
+ cerr << "System error " <<
+ e.errcode() << " (" << e.errmsg() << ")" << endl;
+ } catch (bib1Exception &e) {
+ cerr << "BIB-1 error " <<
+ e.errcode() << " (" << e.errmsg() << "): " << e.addinfo() << endl;
+ } catch (queryException &e) {
+ cerr << "Query error " <<
+ e.errcode() << " (" << e.errmsg() << "): " << e.addinfo() << endl;
+ } catch (exception &e) {
+ cerr << "Error " <<
+ e.errcode() << " (" << e.errmsg() << ")" << endl;
+ }
+ }
+ </programlisting>
+ <para>
+ The heart of this program is the same as in the original version,
+ but it's now wrapped in a <literal>try</literal> block followed by
+ several <literal>catch</literal> blocks which try to give helpful
+ diagnostics if something goes wrong.
+ </para>
+ <para>
+ The first such block diagnoses system-level errors such as memory
+ exhaustion or a network connection being broken by a server's
+ untimely death; the second catches errors at the Z39.50 level,
+ such as a server's report that it can't provide records in USMARC
+ syntax; the third is there in case there's something wrong with
+ the syntax of the query (although in this case it's correct); and
+ finally, the last <literal>catch</literal> block is a
+ belt-and-braces measure to be sure that nothing escapes us.
+ </para>
+ </sect2>
+
+ <sect2 id="exception.references">
+ <title>References</title>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <ulink url="http://zoom.z3950.org/api/zoom-1.3.html#3.7"
+ >Section 3.7 (Exception) of the ZOOM Abstract API</ulink>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <ulink url="&url.z39.50.diagnostics;">Bib-1 Diagnostics</ulink> on the
+ <ulink url="&url.z39.50;">Z39.50 Maintenance Agency</ulink> site.
+ </para>
+ </listitem>
+ </itemizedlist>
<para>
- ### discusson
+ Because C does not support exceptions, ZOOM-C has no API element
+ that corresponds directly with ZOOM-C++'s
+ <literal>exception</literal> class and its subclasses. The
+ closest thing is the <literal>ZOOM_connection_error</literal>
+ function described in
+ <ulink url="&url.yaz.zoom.connections;"
+ >The Connections section</ulink> of the documentation.
</para>
</sect2>
</sect1>
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
- sgml-parent-document: "zebra.xml"
+ sgml-parent-document: "yazpp.xml"
sgml-local-catalogs: nil
sgml-namecase-general:t
End: