Add the promised sample program with error checking.
[yazpp-moved-to-github.git] / doc / zoom.xml
1 <chapter id="zoom">
2  <!-- $Id: zoom.xml,v 1.7 2002-10-11 12:19:09 mike Exp $ -->
3  <title>ZOOM-C++</title>
4
5
6  <sect1 id="zoom-introduction">
7   <title>Introduction</title>
8   <para>
9    <ulink url="http://zoom.z3950.org/">ZOOM</ulink>
10    is the emerging standard API for information retrieval programming
11    using the Z39.50 protocol.  ZOOM's
12    <ulink url="http://zoom.z3950.org/api/">Abstract API</ulink>
13    specifies semantics for classes representing key IR concepts such as
14    connections, queries, result sets and records; and there are various
15    <ulink url="http://zoom.z3950.org/bind/">bindings</ulink>
16    specifying how those concepts should be represented in various
17    programming languages.
18   </para>
19   <para>
20    The Yaz++ library includes an implementation of the <ulink
21    url="http://zoom.z3950.org/bind/cplusplus/"
22         >C++ binding</ulink>
23    for ZOOM, enabling quick, easy development of client applications.
24   </para>
25   <para>
26    For example, here is a tiny Z39.50 client that fetches and displays
27    the MARC record for Farlow & Brett Surman's
28    <citetitle>The Complete Dinosaur</citetitle>
29    from the Library of Congress's Z39.50 server:
30   </para>
31   <programlisting>
32     #include &lt;iostream&gt;
33     #include &lt;yaz++/zoom.h&gt;
34
35     using namespace ZOOM;
36
37     int main(int argc, char **argv)
38     {
39         connection conn("z3950.loc.gov", 7090);
40         conn.option("databaseName", "Voyager");
41         conn.option("preferredRecordSyntax", "USMARC");
42         resultSet rs(conn, prefixQuery("@attr 1=7 0253333490"));
43         const record *rec = rs.getRecord(0);
44         cout &lt;&lt; rec-&gt;render() &lt;&lt; endl;
45     }
46   </programlisting>
47   <para>
48    (Note that, for the sake of simplicity, this program does not check
49    for errors: we show a more robust version of the same program
50    <link linkend="revised-sample">later</link>.)
51   </para>
52   <para>
53    Yaz++'s implementation of the C++ binding is a thin layer over Yaz's
54    implementation of the C binding.  For information on the supported
55    options and other such details, see the ZOOM-C documentation, which
56    can be found on-line at
57    <ulink url="http://www.indexdata.dk/yaz/doc/zoom.php"/>
58   </para>
59   <para>
60    All of the classes defined by ZOOM-C++ are in the
61    <literal>ZOOM</literal> namespace.  We will now consider
62    the five main classes in turn:
63
64    <itemizedlist>
65     <listitem>
66      <para>
67       <literal>connection</literal>
68      </para>
69     </listitem>
70
71     <listitem>
72      <para>
73       <literal>query</literal> and its subclasses
74       <literal>prefixQuery</literal> and
75       <literal>CCLQuery</literal>
76      </para>
77     </listitem>
78
79     <listitem>
80      <para>
81       <literal>resultSet</literal>
82      </para>
83     </listitem>
84
85     <listitem>
86      <para>
87       <literal>record</literal>
88      </para>
89     </listitem>
90
91     <listitem>
92      <para>
93       <literal>exception</literal> and its subclasses
94       <literal>systemException</literal>,
95       <literal>bib1Exception</literal> and
96       <literal>queryException</literal>
97      </para>
98     </listitem>
99    </itemizedlist>
100   </para>
101  </sect1>
102
103
104  <sect1 id="zoom-connection">
105   <title><literal>ZOOM::connection</literal></title>
106   <para>
107    A <literal>ZOOM::connection</literal> object represents an open
108    connection to a Z39.50 server.  Such a connection is forged by
109    constructing a <literal>connection</literal> object.
110   </para>
111   <para>
112    The class has this declaration:
113   </para>
114   <synopsis>
115     class connection {
116     public:
117       connection (const char *hostname, int portnum);
118       ~connection ();
119       const char *option (const char *key) const;
120       const char *option (const char *key, const char *val);
121     };
122   </synopsis>
123   <para>
124    When a new <literal>connection</literal> is created, the hostname
125    and port number of a Z39.50 server must be supplied, and the
126    network connection is forged and wrapped in the new object.  If the
127    connection can't be established - perhaps because the hostname
128    couldn't be resolved, or there is no server listening on the
129    specified port - then an
130    <link linkend="zoom-exception"><literal>exception</literal></link>
131    is thrown.
132   </para>
133   <para>
134    The only other methods on a <literal>connection</literal> object
135    are for getting and setting options.  Any name-value pair of
136    strings may be set as options, and subsequently retrieved, but
137    certain options have special meanings which are understood by the
138    ZOOM code and affect the behaviour of the object that carries
139    them.  For example, the value of the
140    <literal>databaseName</literal> option is used as the name of the
141    database to query when a search is executed against the
142    <literal>connection</literal>.  For a full list of such special
143    options, see the ZOOM abstract API and the ZOOM-C documentation
144    (links below).
145   </para>
146
147   <sect2>
148    <title>References</title>
149    <itemizedlist>
150     <listitem>
151      <para>
152       <ulink url="http://zoom.z3950.org/api/zoom-1.3.html#3.2"
153         >Section 3.2 (Connection) of the ZOOM Abstract API</ulink>
154      </para>
155     </listitem>
156     <listitem>
157      <para>
158       <ulink url="http://www.indexdata.dk/yaz/doc/zoom.php#zoom.connections"
159         >The Connections section of the ZOOM-C documentation</ulink>
160      </para>
161     </listitem>
162    </itemizedlist>
163   </sect2>
164  </sect1>
165
166
167  <sect1 id="zoom-query">
168   <title><literal>ZOOM::query</literal> and subclasses</title>
169   <para>
170    The <literal>ZOOM::query</literal> class is a virtual base class,
171    representing a query to be submitted to a server.  This class has
172    no methods, but two (so far) concrete subclasses, each implementing
173    a specific query notation.
174   </para>
175
176   <sect2>
177    <title><literal>ZOOM::prefixQuery</literal></title>
178    <synopsis>
179     class prefixQuery : public query {
180     public:
181       prefixQuery (const char *pqn);
182       ~prefixQuery ();
183     };
184    </synopsis>
185    <para>
186     This class enables a query to be created by compiling Yaz's
187     cryptic but powerful
188     <ulink url="http://www.indexdata.dk/yaz/doc/tools.php#PQF"
189         >Prefix Query Notation (PQN)</ulink>.
190    </para>
191   </sect2>
192
193   <sect2>
194    <title><literal>ZOOM::CCLQuery</literal></title>
195    <synopsis>
196     class CCLQuery : public query {
197     public:
198       CCLQuery (const char *ccl, void *qualset);
199       ~CCLQuery ();
200     };
201    </synopsis>
202    <para>
203     This class enables a query to be created using the simpler but
204     less expressive
205     <ulink url="http://www.indexdata.dk/yaz/doc/tools.php#CCL"
206         >Common Command Language (CCL)</ulink>.
207     The qualifiers recognised by the CCL parser are specified in an
208     external configuration file in the format described by the Yaz
209     documentation.
210    </para>
211    <para>
212     If query construction fails for either type of
213     <literal>query</literal> object - typically because the query
214     string itself is not valid PQN or CCL - then an
215     <link linkend="zoom-exception"><literal>exception</literal></link>
216     is thrown.
217    </para>
218   </sect2>
219
220   <sect2>
221    <title>Discussion</title>
222    <para>
223     It will be readily recognised that these objects have no methods
224     other than their constructors: their only role in life is to be
225     used in searching, by being passed to the
226     <literal>resultSet</literal> class's constructor.
227    </para>
228    <para>
229     Given a suitable set of CCL qualifiers, the following pairs of
230     queries are equivalent:
231    </para>
232    <screen>
233     prefixQuery("dinosaur");
234     CCLQuery("dinosaur");
235
236     prefixQuery("@and complete dinosaur");
237     CCLQuery("complete and dinosaur");
238
239     prefixQuery("@and complete @or dinosaur pterosaur");
240     CCLQuery("complete and (dinosaur or pterosaur)");
241
242     prefixQuery("@attr 1=7 0253333490");
243     CCLQuery("isbn=0253333490");
244    </screen>
245   </sect2>
246
247   <sect2>
248    <title>References</title>
249    <itemizedlist>
250     <listitem>
251      <para>
252       <ulink url="http://zoom.z3950.org/api/zoom-1.3.html#3.3"
253         >Section 3.3 (Query) of the ZOOM Abstract API</ulink>
254      </para>
255     </listitem>
256     <listitem>
257      <para>
258       <ulink url="http://www.indexdata.dk/yaz/doc/zoom.query.php"
259         >The Queries section of the ZOOM-C documentation</ulink>
260      </para>
261     </listitem>
262    </itemizedlist>
263   </sect2>
264  </sect1>
265
266
267  <sect1 id="zoom-resultset">
268   <title><literal>ZOOM::resultSet</literal></title>
269   <para>
270    A <literal>ZOOM::resultSet</literal> object represents a set of
271    records identified by a query that has been executed against a
272    particular connection.  The sole purpose of both
273    <literal>connection</literal> and <literal>query</literal> objects
274    is that they can be used to create new
275    <literal>resultSet</literal>s - that is, to perform a search on the
276    server on the remote end of the connection.
277   </para>
278   <para>
279    The class has this declaration:
280   </para>
281   <synopsis>
282     class resultSet {
283     public:
284       resultSet (connection &amp;c, const query &amp;q);
285       ~resultSet ();
286       const char *option (const char *key) const;
287       const char *option (const char *key, const char *val);
288       size_t size () const;
289       const record *getRecord (size_t i) const;
290     };
291   </synopsis>
292   <para>
293    New <literal>resultSet</literal>s are created by the constructor,
294    which is passed a <literal>connection</literal>, indicating the
295    server on which the search is to be performed, and a
296    <literal>query</literal>, indicating what search to perform.  If
297    the search fails - for example, because the query uses attributes
298    that the server doesn't implement - then an
299    <link linkend="zoom-exception"><literal>exception</literal></link>
300    is thrown.
301   </para>
302   <para>
303    Like <literal>connection</literal>s, <literal>resultSet</literal>
304    objects can carry name-value options.  The special options which
305    affect ZOOM-C++'s behaviour are the same as those for ZOOM-C and
306    are described in its documentation (link below).  In particular,
307    the <literal>preferredRecordSyntax</literal> option may be set to
308    a string such as ``USMARC'', ``SUTRS'' etc. to indicate what the
309    format in which records should be retrieved; and the
310    <literal>elementSetName</literal> option indicates whether brief
311    records (``B''), full records (``F'') or some other composition
312    should be used.
313   </para>
314   <para>
315    The <literal>size()</literal> method returns the number of records
316    in the result set.  Zero is a legitimate value: a search that finds
317    no records is not the same as a search that fails.
318   </para>
319   <para>
320    Finally, the <literal>getRecord</literal> method returns the
321    <parameter>i</parameter>th record from the result set, where
322    <parameter>i</parameter> is zero-based: that is, legitmate values
323    range from zero up to one less than the result-set size.  If the
324    method fails, for example because the requested record is out of
325    range, it <literal>throw</literal>s an
326    <link linkend="zoom-exception"><literal>exception</literal></link>.
327   </para>
328
329   <sect2>
330    <title>References</title>
331    <itemizedlist>
332     <listitem>
333      <para>
334       <ulink url="http://zoom.z3950.org/api/zoom-1.3.html#3.4"
335         >Section 3.4 (Result Set) of the ZOOM Abstract API</ulink>
336      </para>
337     </listitem>
338     <listitem>
339      <para>
340       <ulink url="http://www.indexdata.dk/yaz/doc/zoom.resultsets.php"
341         >The Result Sets section of the ZOOM-C documentation</ulink>
342      </para>
343     </listitem>
344    </itemizedlist>
345   </sect2>
346  </sect1>
347
348
349  <sect1 id="zoom-record">
350   <title><literal>ZOOM::record</literal></title>
351   <para>
352    A <literal>ZOOM::record</literal> object represents a chunk of data
353    from a <literal>resultSet</literal> returned from a server.
354   </para>
355   <para>
356    The class has this declaration:
357   </para>
358   <synopsis>
359     class record {
360     public:
361       ~record ();
362       enum syntax {
363         UNKNOWN, GRS1, SUTRS, USMARC, UKMARC, XML
364       };
365       record *clone () const;
366       syntax recsyn () const;
367       const char *render () const;
368       const char *rawdata () const;
369     };
370   </synopsis>
371   <para>
372    Records returned from Z39.50 servers are encoded using a record
373    syntax: the various national MARC formats are commonly used for
374    bibliographic data, GRS-1 or XML for complex structured data, SUTRS
375    for simple human-readable text, etc.  The
376    <literal>record::syntax</literal> enumeration specifies constants
377    representing common record syntaxes, and the
378    <literal>recsyn()</literal> method returns the value corresponding
379    to the record-syntax of the record on which it is invoked.
380    <note>
381     <para>
382      Because this interface uses an enumeration, it is difficult to
383      extend to other record syntaxes - for example, DANMARC, the MARC
384      variant widely used in Denmark.  We might either grow the
385      enumeration substantially, or change the interface to return
386      either an integer or a string.
387     </para>
388    </note>
389   </para>
390   <para>
391    The simplest thing to do with a retrieved record is simply to
392    <literal>render()</literal> it.  This returns a human-readable, but
393    not necessarily very pretty, representation of the contents of the
394    record.  This is useful primarily for testing and debugging, since
395    the application has no control over how the record appears.
396    (The application must <emphasis>not</emphasis>
397    <literal>delete</literal> the returned string - it is ``owned'' by
398    the record object.)
399   </para>
400   <para>
401    More sophisticated applications will want to deal with the raw data
402    themselves: the <literal>rawdata()</literal> method returns it.
403    Its format will vary depending on the record syntax: SUTRS, MARC
404    and XML records are returned ``as is'', and GRS-1 records as a
405    pointer to their top-level node, which is a
406    <literal>Z_GenericRecord</literal> structure as defined in the
407    <literal>&lt;yaz/z-grs.h&gt;</literal> header file.
408    (The application must <emphasis>not</emphasis>
409    <literal>delete</literal> the returned data - it is ``owned'' by
410    the record object.)
411   </para>
412   <para>
413    Perceptive readers will notice that there are no methods for access
414    to individual fields within a record.  That's because the different
415    record syntaxes are so different that there is no even a uniform
416    notion of what a field is across them all, let alone a sensible way
417    to implement such a function.  Fetch the raw data instead, and pick
418    it apart ``by hand''.
419   </para>
420
421   <sect2>
422    <title>Memory Management</title>
423    <para>
424     The <literal>record</literal> obejcts returned from
425     <literal>resultSet::getRecord()</literal> are ``owned'' by the
426     result set object: that means that the application is not
427     responsible for <literal>delete</literal>ing them - each
428     <literal>record</literal> is automatically deallocated when the
429     <literal>resultSet</literal> that owns it is
430     <literal>delete</literal>d.
431    </para>
432    <para>
433     Usually that's what you want: it means that you can easily fetch a
434     record, use it and forget all about it, like this:
435    </para>
436    <programlisting>
437     resultSet rs(conn, query);
438     cout &lt;&lt; rs.getRecord(0)-&gt;render();
439    </programlisting>
440    <para>
441     But sometimes you want a <literal>record</literal> to live on past
442     the lifetime of the <literal>resultSet</literal> from which it was
443     fetched.  In this case, the <literal>clone(f)</literal> method can
444     be used to make an autonomous copy.  The application must
445     <literal>delete</literal> it when it doesn't need it any longer:
446    </para>
447    <programlisting>
448     record *rec;
449     {
450         resultSet rs(conn, query);
451         rec = rs.getRecord(0)-&gt;clone();
452         // `rs' goes out of scope here, and is deleted
453     }
454     cout &lt;&lt; rec-&gt;render();
455     delete rec;
456    </programlisting>
457   </sect2>
458
459   <sect2>
460    <title>References</title>
461    <itemizedlist>
462     <listitem>
463      <para>
464       <ulink url="http://zoom.z3950.org/api/zoom-1.3.html#3.5"
465         >Section 3.5 (Record) of the ZOOM Abstract API</ulink>
466      </para>
467     </listitem>
468     <listitem>
469      <para>
470       <ulink url="http://www.indexdata.dk/yaz/doc/zoom.records.php"
471         >The Records section of the ZOOM-C documentation</ulink>
472      </para>
473     </listitem>
474    </itemizedlist>
475   </sect2>
476  </sect1>
477
478
479  <sect1 id="zoom-exception">
480   <title><literal>ZOOM::exception</literal> and subclasses</title>
481   <para>
482    The <literal>ZOOM::exception</literal> class is a virtual base
483    class, representing a diagnostic generated by the ZOOM-C++ library
484    or returned from a server.  Its subclasses represent particular
485    kinds of error.
486   </para>
487   <para>
488    When any of the ZOOM methods fail, they respond by
489    <literal>throw</literal>ing an object of type
490    <literal>exception</literal> or one of its subclasses.  This most
491    usually happens with the <literal>connection</literal> constructor,
492    the various query constructors, the <literal>resultSet</literal>
493    constructor (which is actually the searching method) and
494    <literal>resultSet::getRecord()</literal>.
495   </para>
496   <para>
497     The base class has this declaration:
498   </para>
499   <synopsis>
500     class exception {
501     public:
502       exception (int code);
503       int errcode () const;
504       const char *errmsg () const;
505     };
506   </synopsis>
507   <para>
508    It has three concrete subclasses:
509   </para>
510
511   <sect2>
512    <title><literal>ZOOM::systemException</literal></title>
513    <synopsis>
514     class systemException: public exception {
515     public:
516       systemException ();
517       int errcode () const;
518       const char *errmsg () const;
519     };
520    </synopsis>
521    <para>
522     Represents a ``system error'', typically indicating that a system
523     call failed - often in the low-level networking code that
524     underlies Z39.50.  <literal>errcode()</literal> returns the value
525     that the system variable <literal>errno</literal> had at the time
526     the exception was constructed; and <literal>errmsg()</literal>
527     returns a human-readable error-message corresponidng to that error
528     code.
529    </para>
530   </sect2>
531
532   <sect2>
533    <title><literal>ZOOM::bib1Exception</literal></title>
534    <synopsis>
535     class bib1Exception: public exception {
536     public:
537       bib1Exception (int errcode, const char *addinfo);
538       int errcode () const;
539       const char *errmsg () const;
540       const char *addinfo () const;
541     };
542    </synopsis>
543    <para>
544     Represents an error condition communicated by a Z39.50 server.
545     <literal>errcode()</literal> returns the BIB-1 diagnostic code of
546     the error, and <literal>errmsg()</literal> a human-readable error
547     message corresponding to that code.  <literal>addinfo()</literal>
548     returns any additional information associated with the error.
549    </para>
550    <para>
551     For example, if a ZOOM application tries to search in the
552     ``Voyager'' database of a server that does not have a database of
553     that name, a <literal>bib1Exception</literal> will be thrown in
554     which <literal>errcode()</literal> returns 109,
555     <literal>errmsg()</literal> returns the corresponding error
556     message ``Database unavailable'' and <literal>addinfo()</literal>
557     returns the name of the requested, but unavailable, database.
558    </para>
559   </sect2>
560
561   <sect2>
562    <title><literal>ZOOM::queryException</literal></title>
563    <synopsis>
564     class queryException: public exception {
565     public:
566       static const int PREFIX = 1;
567       static const int CCL = 2;
568       queryException (int qtype, const char *source);
569       int errcode () const;
570       const char *errmsg () const;
571       const char *addinfo () const;
572     };
573    </synopsis>
574    <para>
575     This class represents an error in parsing a query into a form that
576     a Z39.50 can understand.  It must be created with the
577     <literal>qtype</literal> parameter equal to one of the query-type
578     constants, which can be retrieved via the
579     <literal>errcode()</literal> method; <literal>errmsg()</literal>
580     returns an error-message specifying which kind of query was
581     malformed; and <literal>addinfo()</literal> returns a copy of the
582     query itself (that is, the value of <literal>source</literal> with
583     which the exception object was created.)
584    </para>
585   </sect2>
586
587   <sect2 id="revised-sample">
588    <title>Revised Sample Program</title>
589    <para>
590     Now we can revise the sample program from the
591     <link linkend="zoom-introduction">introduction</link>
592     to catch exceptions and report any errors:
593    </para>
594    <programlisting>
595     /* g++ -o zoom-c++-hw zoom-c++-hw.cpp -lyaz++ -lyaz */
596
597     #include &lt;iostream&gt;
598     #include &lt;yaz++/zoom.h&gt;
599
600     using namespace ZOOM;
601
602     int main(int argc, char **argv)
603     {
604         try {
605             connection conn("z3950.loc.gov", 7090);
606             conn.option("databaseName", "Voyager");
607             conn.option("preferredRecordSyntax", "USMARC");
608             resultSet rs(conn, prefixQuery("@attr 1=7 0253333490"));
609             const record *rec = rs.getRecord(0);
610             cout &lt;&lt; rec-&gt;render() &lt;&lt; endl;
611         } catch (systemException &amp;e) {
612             cerr &lt;&lt; "System error " &lt;&lt;
613                 e.errcode() &lt;&lt; " (" &lt;&lt; e.errmsg() &lt;&lt; ")" &lt;&lt; endl;
614         } catch (bib1Exception &amp;e) {
615             cerr &lt;&lt; "BIB-1 error " &lt;&lt; 
616                 e.errcode() &lt;&lt; " (" &lt;&lt; e.errmsg() &lt;&lt; "): " &lt;&lt; e.addinfo() &lt;&lt; endl;
617         } catch (queryException &amp;e) {
618             cerr &lt;&lt; "Query error " &lt;&lt;
619                 e.errcode() &lt;&lt; " (" &lt;&lt; e.errmsg() &lt;&lt; "): " &lt;&lt; e.addinfo() &lt;&lt; endl;
620         } catch (exception &amp;e) {
621             cerr &lt;&lt; "Error " &lt;&lt;
622                 e.errcode() &lt;&lt; " (" &lt;&lt; e.errmsg() &lt;&lt; ")" &lt;&lt; endl;
623         }
624     }
625    </programlisting>
626    <para>
627     The heart of this program is the same as in the original version,
628     but it's now wrapped in a <literal>try</literal> block followed by
629     several <literal>catch</literal> blocks which try to give helpful
630     diagnostics if something goes wrong.
631    </para>
632    <para>
633     The first such block diagnoses system-level errors such as memory
634     exhaustion or a network connection being broken by a server's
635     untimely death; the second catches errors at the Z39.50 level,
636     such as a server's report that it can't provide records in USMARC
637     syntax; the third is there in case there's something wrong with
638     the syntax of the query (although in this case it's correct); and
639     finally, the last <literal>catch</literal> block is a
640     belt-and-braces measure to be sure that nothing escapes us.
641    </para>
642   </sect2>
643
644   <sect2>
645    <title>References</title>
646    <itemizedlist>
647     <listitem>
648      <para>
649       <ulink url="http://zoom.z3950.org/api/zoom-1.3.html#3.7"
650         >Section 3.7 (Exception) of the ZOOM Abstract API</ulink>
651      </para>
652     </listitem>
653     <listitem>
654      <para>
655       <ulink url="http://lcweb.loc.gov/z3950/agency/defns/bib1diag.html"
656         >Bib-1 Diagnostics</ulink> on the
657       <ulink url="http://lcweb.loc.gov/z3950/agency/"
658         >Z39.50 Maintenance Agency</ulink> site.
659      </para>
660     </listitem>
661    </itemizedlist>
662    <para>
663     Because C does not support exceptions, ZOOM-C has no API element
664     that corresponds directly with ZOOM-C++'s
665     <literal>exception</literal> class and its subclasses.  The
666     closest thing is the <literal>ZOOM_connection_error</literal>
667     function described in
668     <ulink url="http://www.indexdata.dk/yaz/doc/zoom.php#zoom.connections"
669         >The Connections section</ulink> of the documentation.
670    </para>
671   </sect2>
672  </sect1>
673
674 </chapter>
675
676  <!-- Keep this comment at the end of the file
677  Local variables:
678  mode: sgml
679  sgml-omittag:t
680  sgml-shorttag:t
681  sgml-minimize-attributes:nil
682  sgml-always-quote-attributes:t
683  sgml-indent-step:1
684  sgml-indent-data:t
685  sgml-parent-document: "zebra.xml"
686  sgml-local-catalogs: nil
687  sgml-namecase-general:t
688  End:
689  -->