Clean up 2.1 branch.
[Evergreen-DocBook.git] / development / OpenSRF_intro.xml
diff --git a/development/OpenSRF_intro.xml b/development/OpenSRF_intro.xml
deleted file mode 100644 (file)
index f345997..0000000
+++ /dev/null
@@ -1,1738 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>\r
-<chapter xml:id="opensrf" xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="EN"\r
-    xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:xlink="http://www.w3.org/1999/xlink">\r
-       <chapterinfo>\r
-               <title>OpenSRF</title>\r
-       </chapterinfo>\r
-       <abstract id="openSRF_abstract">\r
-               <simpara>One of the claimed advantages of\r
-               Evergreen over alternative integrated library systems is the underlying Open\r
-               Service Request Framework (OpenSRF, pronounced "open surf") architecture. This\r
-               article introduces OpenSRF, demonstrates how to build OpenSRF services through\r
-               simple code examples, and explains the technical foundations on which OpenSRF\r
-               is built. This chapter was taken from Dan Scott's <emphasis>Easing gently into OpenSRF</emphasis> article, June, 2010.</simpara>\r
-       </abstract>\r
-       <section id="_introducing_opensrf">\r
-               <title>Introducing OpenSRF</title>\r
-       <indexterm><primary>OpenSRF</primary></indexterm>\r
-               <simpara>OpenSRF is a message routing network that offers scalability and failover\r
-               support for individual services and entire servers with minimal development and\r
-               deployment overhead. You can use OpenSRF to build loosely-coupled applications\r
-               that can be deployed on a single server or on clusters of geographically\r
-               distributed servers using the same code and minimal configuration changes.\r
-               Although copyright statements on some of the OpenSRF code date back to Mike\r
-               Rylander&#8217;s original explorations in 2000, Evergreen was the first major\r
-               application to be developed with, and to take full advantage of, the OpenSRF\r
-               architecture starting in 2004. The first official release of OpenSRF was 0.1 in\r
-               February 2005 (<ulink url="http://evergreen-ils.org/blog/?p=21">http://evergreen-ils.org/blog/?p=21</ulink>), but OpenSRF&#8217;s development\r
-               continues a steady pace of enhancement and refinement, with the release of\r
-               1.0.0 in October 2008 and the most recent release of 1.2.2 in February 2010.</simpara>\r
-               <simpara>OpenSRF is a distinct break from the architectural approach used by previous\r
-               library systems and has more in common with modern Web applications. The\r
-               traditional "scale-up" approach to serve more transactions is to purchase a\r
-               server with more CPUs and more RAM, possibly splitting the load between a Web\r
-               server, a database server, and a business logic server. Evergreen, however, is\r
-               built on the Open Service Request Framework (OpenSRF) architecture, which\r
-               firmly embraces the "scale-out" approach of spreading transaction load over\r
-               cheap commodity servers. The <ulink url="http://evergreen-ils.org/blog/?p=56">initial GPLS\r
-               PINES hardware cluster</ulink>, while certainly impressive, may have offered the\r
-               misleading impression that Evergreen requires a lot of hardware to run.\r
-               However, Evergreen and OpenSRF easily scale down to a single server; many\r
-               Evergreen libraries run their entire library system on a single server, and\r
-               most OpenSRF and Evergreen development occurs on a virtual machine running on a\r
-               single laptop or desktop image.</simpara>\r
-               <simpara>Another common concern is that the flexibility of OpenSRF&#8217;s distributed\r
-               architecture makes it complex to configure and to write new applications. This\r
-               article demonstrates that OpenSRF itself is an extremely simple architecture on\r
-               which one can easily build applications of many kinds – not just library\r
-               applications – and that you can use a number of different languages to call and\r
-               implement OpenSRF methods with a minimal learning curve. With an application\r
-               built on OpenSRF, when you identify a bottleneck in your application&#8217;s business\r
-               logic layer, you can adjust the number of the processes serving that particular\r
-               bottleneck on each of your servers; or if the problem is that your service is\r
-               resource-hungry, you could add an inexpensive server to your cluster and\r
-               dedicate it to running that resource-hungry service.</simpara>\r
-               <simplesect id="_programming_language_support">\r
-                       <title>Programming language support</title>\r
-                       <simpara>If you need to develop an entirely new OpenSRF service, you can choose from a\r
-                       number of different languages in which to implement that service. OpenSRF\r
-                       client language bindings have been written for C, Java, JavaScript, Perl, and\r
-                       Python, and service language bindings have been written for C, Perl, and Python.\r
-                       This article uses Perl examples as a lowest common denominator programming\r
-                       language. Writing an OpenSRF binding for another language is a relatively small\r
-                       task if that language offers libraries that support the core technologies on\r
-                       which OpenSRF depends:</simpara>\r
-                       <itemizedlist>\r
-                       <listitem>\r
-                       <simpara>\r
-                       <ulink url="http://tools.ietf.org/html/rfc3920">Extensible Messaging and Presence\r
-                       Protocol</ulink> (XMPP, sometimes referred to as Jabber) - provides the base messaging\r
-                       infrastructure between OpenSRF clients and services\r
-                       </simpara>\r
-                       <indexterm><primary>XMPP</primary></indexterm>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       <ulink url="http://json.org">JavaScript Object Notation</ulink> (JSON) - serializes the content\r
-                       of each XMPP message in a standardized and concise format\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       <ulink url="http://memcached.org">memcached</ulink> - provides the caching service\r
-                       </simpara>\r
-                       <indexterm><primary>memcached</primary></indexterm>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       <ulink url="http://tools.ietf.org/html/rfc5424">syslog</ulink> - the standard UNIX logging\r
-                       service\r
-                       </simpara>\r
-                       <indexterm><primary>syslog</primary></indexterm>\r
-                       </listitem>\r
-                       </itemizedlist>\r
-                       <simpara>Unfortunately, the\r
-                       <ulink url="http://evergreen-ils.org/dokuwiki/doku.php?id=osrf-devel:primer">OpenSRF\r
-                       reference documentation</ulink>, although augmented by the\r
-                       <ulink url="http://evergreen-ils.org/dokuwiki/doku.php?id=osrf-devel:terms">OpenSRF\r
-                       glossary</ulink>, blog posts like <ulink url="http://evergreen-ils.org/blog/?p=36">the description\r
-                       of OpenSRF and Jabber</ulink>, and even this article, is not a sufficient substitute\r
-                       for a complete specification on which one could implement a language binding.\r
-                       The recommended option for would-be developers of another language binding is\r
-                       to use the Python implementation as the cleanest basis for a port to another\r
-                       language.</simpara>\r
-                       <indexterm><primary>Python</primary></indexterm>\r
-               </simplesect>\r
-       </section>\r
-       <section id="writing_an_opensrf_service">\r
-               <title>Writing an OpenSRF Service</title>\r
-               <simpara>Imagine an application architecture in which 10 lines of Perl or Python, using\r
-               the data types native to each language, are enough to implement a method that\r
-               can then be deployed and invoked seamlessly across hundreds of servers.  You\r
-               have just imagined developing with OpenSRF – it is truly that simple. Under the\r
-               covers, of course, the OpenSRF language bindings do an incredible amount of\r
-               work on behalf of the developer. An OpenSRF application consists of one or more\r
-               OpenSRF services that expose methods: for example, the <literal>opensrf.simple-text</literal>\r
-               <ulink url="http://svn.open-ils.org/trac/OpenSRF/browser/trunk/src/perl/lib/OpenSRF/Application/Demo/SimpleText.pm">demonstration\r
-               service</ulink> exposes the <literal>opensrf.simple-text.split()</literal> and\r
-               <literal>opensrf.simple-text.reverse()</literal> methods. Each method accepts zero or more\r
-               arguments and returns zero or one results. The data types supported by OpenSRF\r
-               arguments and results are typical core language data types: strings, numbers,\r
-               booleans, arrays, and hashes.</simpara>\r
-               <simpara>To implement a new OpenSRF service, perform the following steps:</simpara>\r
-               <orderedlist numeration="arabic">\r
-               <listitem>\r
-               <simpara>\r
-               Include the base OpenSRF support libraries\r
-               </simpara>\r
-               </listitem>\r
-               <listitem>\r
-               <simpara>\r
-               Write the code for each of your OpenSRF methods as separate procedures\r
-               </simpara>\r
-               </listitem>\r
-               <listitem>\r
-               <simpara>\r
-               Register each method\r
-               </simpara>\r
-               </listitem>\r
-               <listitem>\r
-               <simpara>\r
-               Add the service definition to the OpenSRF configuration files\r
-               </simpara>\r
-               </listitem>\r
-               </orderedlist>\r
-               <simpara>For example, the following code implements an OpenSRF service. The service\r
-               includes one method named <literal>opensrf.simple-text.reverse()</literal> that accepts one\r
-               string as input and returns the reversed version of that string:</simpara>\r
-<programlisting language="perl" linenumbering="unnumbered">\r
-#!/usr/bin/perl\r
-\r
-package OpenSRF::Application::Demo::SimpleText;\r
-\r
-use strict;\r
-\r
-use OpenSRF::Application;\r
-use parent qw/OpenSRF::Application/;\r
-\r
-sub text_reverse {\r
-    my ($self , $conn, $text) = @_;\r
-    my $reversed_text = scalar reverse($text);\r
-    return $reversed_text;\r
-}\r
-\r
-__PACKAGE__-&gt;register_method(\r
-    method    =&gt; 'text_reverse',\r
-    api_name  =&gt; 'opensrf.simple-text.reverse'\r
-);\r
-</programlisting>\r
-               <simpara>Ten lines of code, and we have a complete OpenSRF service that exposes a single\r
-               method and could be deployed quickly on a cluster of servers to meet your\r
-               application&#8217;s ravenous demand for reversed strings! If you&#8217;re unfamiliar with\r
-               Perl, the <literal>use OpenSRF::Application; use parent qw/OpenSRF::Application/;</literal>\r
-               lines tell this package to inherit methods and properties from the\r
-               <literal>OpenSRF::Application</literal> module. For example, the call to\r
-               <literal>__PACKAGE__-&gt;register_method()</literal> is defined in <literal>OpenSRF::Application</literal> but due to\r
-               inheritance is available in this package (named by the special Perl symbol\r
-               <literal>__PACKAGE__</literal> that contains the current package name). The <literal>register_method()</literal>\r
-               procedure is how we introduce a method to the rest of the OpenSRF world.</simpara>\r
-               <simplesect id="serviceRegistration">\r
-                       <title>Registering a service with the OpenSRF configuration files</title>\r
-                       <simpara>Two files control most of the configuration for OpenSRF:</simpara>\r
-                       <itemizedlist>\r
-                       <listitem>\r
-                       <simpara>\r
-                       <literal>opensrf.xml</literal> contains the configuration for the service itself, as well as\r
-                       a list of which application servers in your OpenSRF cluster should start\r
-                       the service.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       <literal>opensrf_core.xml</literal> (often referred to as the "bootstrap configuration"\r
-                       file) contains the OpenSRF networking information, including the XMPP server\r
-                       connection credentials for the public and private routers. You only need to touch\r
-                       this for a new service if the new service needs to be accessible via the\r
-                       public router.\r
-                       </simpara>\r
-                       <indexterm><primary>configuration files</primary><secondary>opensrf_core.xml</secondary></indexterm>\r
-                       </listitem>\r
-                       </itemizedlist>\r
-                       <simpara>Begin by defining the service itself in <literal>opensrf.xml</literal>. To register the\r
-                       <literal>opensrf.simple-text</literal> service, add the following section to the <literal>&lt;apps&gt;</literal>\r
-                       element (corresponding to the XPath <literal>/opensrf/default/apps/</literal>):</simpara>\r
-                       <indexterm><primary>configuration files</primary><secondary>opensrf.xml</secondary></indexterm>\r
-<programlisting language="xml" linenumbering="unnumbered">\r
-&lt;apps&gt;\r
-  &lt;opensrf.simple-text&gt; <co id="CO1-1"/> \r
-    &lt;keepalive&gt;3&lt;/keepalive&gt;<co id="CO1-2"/> \r
-    &lt;stateless&gt;1&lt;/stateless&gt;<co id="CO1-3"/>\r
-    &lt;language&gt;perl&lt;/language&gt;<co id="CO1-4"/> \r
-    &lt;implementation&gt;OpenSRF::Application::Demo::SimpleText&lt;/implementation&gt;<co id="CO1-5"/> \r
-    &lt;max_requests&gt;100&lt;/max_requests&gt;<co id="CO1-6"/> \r
-    &lt;unix_config&gt;\r
-      &lt;max_requests&gt;1000&lt;/max_requests&gt; <co id="CO1-7"/> \r
-      &lt;unix_log&gt;opensrf.simple-text_unix.log&lt;/unix_log&gt; <co id="CO1-8"/> \r
-      &lt;unix_sock&gt;opensrf.simple-text_unix.sock&lt;/unix_sock&gt;<co id="CO1-9"/> \r
-      &lt;unix_pid&gt;opensrf.simple-text_unix.pid&lt;/unix_pid&gt; <co id="CO1-10"/> \r
-      &lt;min_children&gt;5&lt;/min_children&gt;  <co id="CO1-11"/> \r
-      &lt;max_children&gt;15&lt;/max_children&gt;<co id="CO1-12"/> \r
-      &lt;min_spare_children&gt;2&lt;/min_spare_children&gt;<co id="CO1-13"/> \r
-      &lt;max_spare_children&gt;5&lt;/max_spare_children&gt; <co id="CO1-14"/> \r
-    &lt;/unix_config&gt;\r
-  &lt;/opensrf.simple-text&gt;\r
-\r
-  &lt;!-- other OpenSRF services registered here... --&gt;\r
-&lt;/apps&gt;\r
-</programlisting>\r
-                       <calloutlist>\r
-                       <callout arearefs="CO1-1">\r
-                       <simpara>\r
-                       The element name is the name that the OpenSRF control scripts use to refer\r
-                       to the service.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO1-2">\r
-                       <simpara>\r
-                       The <literal>&lt;keepalive&gt;</literal> element specifies the interval (in seconds) between\r
-                       checks to determine if the service is still running.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO1-3">\r
-                       <simpara>\r
-                       The <literal>&lt;stateless&gt;</literal> element specifies whether OpenSRF clients can call\r
-                       methods from this service without first having to create a connection to a\r
-                       specific service backend process for that service. If the value is <literal>1</literal>, then\r
-                       the client can simply issue a request and the router will forward the request\r
-                       to an available service and the result will be returned directly to the client.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO1-4">\r
-                       <simpara>\r
-                       The <literal>&lt;language&gt;</literal> element specifies the programming language in which the\r
-                       service is implemented.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO1-5">\r
-                       <simpara>\r
-                       The <literal>&lt;implementation&gt;</literal> element pecifies the name of the library or module\r
-                       in which the service is implemented.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO1-6">\r
-                       <simpara>\r
-                       (C implementations only): The <literal>&lt;max_requests&gt;</literal> element, as a direct child\r
-                       of the service element name, specifies the maximum number of requests a process\r
-                       serves before it is killed and replaced by a new process.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO1-7">\r
-                       <simpara>\r
-                       (Perl implementations only): The <literal>&lt;max_requests&gt;</literal> element, as a direct\r
-                       child of the <literal>&lt;unix_config&gt;</literal> element, specifies the maximum number of requests\r
-                       a process serves before it is killed and replaced by a new process.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO1-8">\r
-                       <simpara>\r
-                       The <literal>&lt;unix_log&gt;</literal> element specifies the name of the log file for\r
-                       language-specific log messages such as syntax warnings.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO1-9">\r
-                       <simpara>\r
-                       The <literal>&lt;unix_sock&gt;</literal> element specifies the name of the UNIX socket used for\r
-                       inter-process communications.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO1-10">\r
-                       <simpara>\r
-                       The <literal>&lt;unix_pid&gt;</literal> element specifies the name of the PID file for the\r
-                       master process for the service.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO1-11">\r
-                       <simpara>\r
-                       The <literal>&lt;min_children&gt;</literal> element specifies the minimum number of child\r
-                       processes that should be running at any given time.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO1-12">\r
-                       <simpara>\r
-                       The <literal>&lt;max_children&gt;</literal> element specifies the maximum number of child\r
-                       processes that should be running at any given time.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO1-13">\r
-                       <simpara>\r
-                       The <literal>&lt;min_spare_children&gt;</literal> element specifies the minimum number of idle\r
-                       child processes that should be available to handle incoming requests.  If there\r
-                       are fewer than this number of spare child processes, new processes will be\r
-                       spawned.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO1-14">\r
-                       <simpara>\r
-                       The`&lt;max_spare_children&gt;` element specifies the maximum number of idle\r
-                       child processes that should be available to handle incoming requests. If there\r
-                       are more than this number of spare child processes, the extra processes will be\r
-                       killed.\r
-                       </simpara>\r
-                       </callout>\r
-                       </calloutlist>\r
-                       <simpara>To make the service accessible via the public router, you must also\r
-                       edit the <literal>opensrf_core.xml</literal> configuration file to add the service to the list\r
-                       of publicly accessible services:</simpara>\r
-                       <formalpara><title>Making a service publicly accessible in <literal>opensrf_core.xml</literal></title><para>\r
-<programlisting language="xml" linenumbering="unnumbered">\r
-&lt;router&gt;<co id="CO2-1"/> \r
-    &lt;!-- This is the public router. On this router, we only register applications\r
-     which should be accessible to everyone on the opensrf network --&gt;\r
-    &lt;name&gt;router&lt;/name&gt;\r
-    &lt;domain&gt;public.localhost&lt;/domain&gt;<co id="CO2-2"/>\r
-    &lt;services&gt;\r
-       &lt;service&gt;opensrf.math&lt;/service&gt;\r
-       &lt;service&gt;opensrf.simple-text&lt;/service&gt; <co id="CO2-3"/> \r
-    &lt;/services&gt;\r
-&lt;/router&gt;\r
-</programlisting>\r
-                       </para></formalpara>\r
-                       <calloutlist>\r
-                       <callout arearefs="CO2-1">\r
-                       <simpara>\r
-                       This section of the <literal>opensrf_core.xml</literal> file is located at XPath\r
-                       <literal>/config/opensrf/routers/</literal>.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO2-2">\r
-                       <simpara>\r
-                       <literal>public.localhost</literal> is the canonical public router domain in the OpenSRF\r
-                       installation instructions.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO2-3">\r
-                       <simpara>\r
-                       Each <literal>&lt;service&gt;</literal> element contained in the <literal>&lt;services&gt;</literal> element\r
-                       offers their services via the public router as well as the private router.\r
-                       </simpara>\r
-                       </callout>\r
-                       </calloutlist>\r
-                       <simpara>Once you have defined the new service, you must restart the OpenSRF Router\r
-                       to retrieve the new configuration and start or restart the service itself.</simpara>\r
-               </simplesect>\r
-               <simplesect id="_calling_an_opensrf_method">\r
-                       <title>Calling an OpenSRF method</title>\r
-                       <indexterm><primary>srfsh</primary></indexterm>\r
-                       <simpara>OpenSRF clients in any supported language can invoke OpenSRF services in any\r
-                       supported language. So let&#8217;s see a few examples of how we can call our fancy\r
-                       new <literal>opensrf.simple-text.reverse()</literal> method:</simpara>\r
-                       <simplesect id="_calling_opensrf_methods_from_the_srfsh_client">\r
-                               <title>Calling OpenSRF methods from the srfsh client</title>\r
-                               <simpara><literal>srfsh</literal> is a command-line tool installed with OpenSRF that you can use to call\r
-                               OpenSRF methods. To call an OpenSRF method, issue the <literal>request</literal> command and\r
-                               pass the OpenSRF service and method name as the first two arguments; then pass\r
-                               one or more JSON objects delimited by commas as the arguments to the method\r
-                               being invoked.</simpara>\r
-                               <simpara>The following example calls the <literal>opensrf.simple-text.reverse</literal> method of the\r
-                               <literal>opensrf.simple-text</literal> OpenSRF service, passing the string <literal>"foobar"</literal> as the\r
-                               only method argument:</simpara>\r
-<programlisting language="sh" linenumbering="unnumbered">\r
-$ srfsh\r
-srfsh # request opensrf.simple-text opensrf.simple-text.reverse "foobar"\r
-\r
-Received Data: "raboof"\r
-\r
-=------------------------------------\r
-Request Completed Successfully\r
-Request Time in seconds: 0.016718\r
-=------------------------------------\r
-</programlisting>\r
-                       </simplesect>\r
-                       <simplesect id="opensrfIntrospection">\r
-                               <title>Getting documentation for OpenSRF methods from the srfsh client</title>\r
-                               <simpara>The <literal>srfsh</literal> client also gives you command-line access to retrieving metadata\r
-                               about OpenSRF services and methods. For a given OpenSRF method, for example,\r
-                               you can retrieve information such as the minimum number of required arguments,\r
-                               the data type and a description of each argument, the package or library in\r
-                               which the method is implemented, and a description of the method. To retrieve\r
-                               the documentation for an opensrf method from <literal>srfsh</literal>, issue the <literal>introspect</literal>\r
-                               command, followed by the name of the OpenSRF service and (optionally) the\r
-                               name of the OpenSRF method. If you do not pass a method name to the <literal>introspect</literal>\r
-                               command, <literal>srfsh</literal> lists all of the methods offered by the service. If you pass\r
-                               a partial method name, <literal>srfsh</literal> lists all of the methods that match that portion\r
-                               of the method name.</simpara>\r
-                               <note><simpara>The quality and availability of the descriptive information for each\r
-                               method depends on the developer to register the method with complete and\r
-                               accurate information. The quality varies across the set of OpenSRF and\r
-                               Evergreen APIs, although some effort is being put towards improving the\r
-                               state of the internal documentation.</simpara></note>\r
-<programlisting language="sh" linenumbering="unnumbered">\r
-srfsh# introspect opensrf.simple-text "opensrf.simple-text.reverse"\r
---&gt; opensrf.simple-text\r
-\r
-Received Data: {\r
-  "__c":"opensrf.simple-text",\r
-  "__p":{\r
-    "api_level":1,\r
-    "stream":0,      <co id="CO3-1"/>\r
-    "object_hint":"OpenSRF_Application_Demo_SimpleText",\r
-    "remote":0,\r
-    "package":"OpenSRF::Application::Demo::SimpleText", <co id="CO3-2"/>\r
-    "api_name":"opensrf.simple-text.reverse",<co id="CO3-3"/>\r
-    "server_class":"opensrf.simple-text",\r
-    "signature":{ <co id="CO3-4"/>\r
-      "params":[  <co id="CO3-5"/>\r
-       {\r
-         "desc":"The string to reverse",\r
-         "name":"text",\r
-         "type":"string"\r
-       }\r
-      ],\r
-      "desc":"Returns the input string in reverse order\n", <co id="CO3-6"/>\r
-      "return":{                                            <co id="CO3-7"/>\r
-       "desc":"Returns the input string in reverse order",\r
-       "type":"string"\r
-      }\r
-    },\r
-    "method":"text_reverse",  <co id="CO3-8"/>\r
-    "argc":1 <co id="CO3-9"/>\r
-  }\r
-}\r
-</programlisting>\r
-                               <calloutlist>\r
-                               <callout arearefs="CO3-1">\r
-                               <simpara>\r
-                               <literal>stream</literal> denotes whether the method supports streaming responses or not.\r
-                               </simpara>\r
-                               </callout>\r
-                               <callout arearefs="CO3-2">\r
-                               <simpara>\r
-                               <literal>package</literal> identifies which package or library implements the method.\r
-                               </simpara>\r
-                               </callout>\r
-                               <callout arearefs="CO3-3">\r
-                               <simpara>\r
-                               <literal>api_name</literal> identifies the name of the OpenSRF method.\r
-                               </simpara>\r
-                               </callout>\r
-                               <callout arearefs="CO3-4">\r
-                               <simpara>\r
-                               <literal>signature</literal> is a hash that describes the parameters for the method.\r
-                               </simpara>\r
-                               </callout>\r
-                               <callout arearefs="CO3-5">\r
-                               <simpara>\r
-                               <literal>params</literal> is an array of hashes describing each parameter in the method;\r
-                               each parameter has a description (<literal>desc</literal>), name (<literal>name</literal>), and type (<literal>type</literal>).\r
-                               </simpara>\r
-                               </callout>\r
-                               <callout arearefs="CO3-6">\r
-                               <simpara>\r
-                               <literal>desc</literal> is a string that describes the method itself.\r
-                               </simpara>\r
-                               </callout>\r
-                               <callout arearefs="CO3-7">\r
-                               <simpara>\r
-                               <literal>return</literal> is a hash that describes the return value for the method; it\r
-                               contains a description of the return value (<literal>desc</literal>) and the type of the\r
-                               returned value (<literal>type</literal>).\r
-                               </simpara>\r
-                               </callout>\r
-                               <callout arearefs="CO3-8">\r
-                               <simpara>\r
-                               <literal>method</literal> identifies the name of the function or method in the source\r
-                               implementation.\r
-                               </simpara>\r
-                               </callout>\r
-                               <callout arearefs="CO3-9">\r
-                               <simpara>\r
-                               <literal>argc</literal> is an integer describing the minimum number of arguments that\r
-                               must be passed to this method.\r
-                               </simpara>\r
-                               </callout>\r
-                               </calloutlist>\r
-                       </simplesect>\r
-                       <simplesect id="_calling_opensrf_methods_from_perl_applications">\r
-                               <title>Calling OpenSRF methods from Perl applications</title>\r
-                               <simpara>To call an OpenSRF method from Perl, you must connect to the OpenSRF service,\r
-                               issue the request to the method, and then retrieve the results.</simpara>\r
-<programlisting language="perl" linenumbering="unnumbered">\r
-#/usr/bin/perl\r
-use strict;\r
-use OpenSRF::AppSession;\r
-use OpenSRF::System;\r
-\r
-OpenSRF::System-&gt;bootstrap_client(config_file =&gt; '/openils/conf/opensrf_core.xml');<co id="CO4-1"/>\r
-\r
-my $session = OpenSRF::AppSession-&gt;create("opensrf.simple-text");<co id="CO4-2"/>\r
-\r
-print "substring: Accepts a string and a number as input, returns a string\n";\r
-my $result = $session-&gt;request("opensrf.simple-text.substring", "foobar", 3);<co id="CO4-3"/>\r
-my $request = $result-&gt;gather(); <co id="CO4-4"/>\r
-print "Substring: $request\n\n";\r
-\r
-print "split: Accepts two strings as input, returns an array of strings\n";\r
-$request = $session-&gt;request("opensrf.simple-text.split", "This is a test", " ");<co id="CO4-5"/>\r
-my $output = "Split: [";\r
-my $element;\r
-while ($element = $request-&gt;recv()) {   <co id="CO4-6"/>\r
-    $output .= $element-&gt;content . ", ";  <co id="CO4-7"/>\r
-}\r
-$output =~ s/, $/]/;\r
-print $output . "\n\n";\r
-\r
-print "statistics: Accepts an array of strings as input, returns a hash\n";\r
-my @many_strings = [\r
-    "First I think I'll have breakfast",\r
-    "Then I think that lunch would be nice",\r
-    "And then seventy desserts to finish off the day"\r
-];\r
-\r
-$result = $session-&gt;request("opensrf.simple-text.statistics", @many_strings); <co id="CO4-8"/>\r
-$request = $result-&gt;gather();    <co id="CO4-9"/>\r
-print "Length: " . $result-&gt;{'length'} . "\n";\r
-print "Word count: " . $result-&gt;{'word_count'} . "\n";\r
-\r
-$session-&gt;disconnect();       <co id="CO4-10"/>\r
-</programlisting>\r
-                               <calloutlist>\r
-                               <callout arearefs="CO4-1">\r
-                               <simpara>\r
-                               The <literal>OpenSRF::System-&gt;bootstrap_client()</literal> method reads the OpenSRF\r
-                               configuration information from the indicated file and creates an XMPP client\r
-                               connection based on that information.\r
-                               </simpara>\r
-                               </callout>\r
-                               <callout arearefs="CO4-2">\r
-                               <simpara>\r
-                               The <literal>OpenSRF::AppSession-&gt;create()</literal> method accepts one argument - the name\r
-                               of the OpenSRF service to which you want to want to make one or more requests -\r
-                               and returns an object prepared to use the client connection to make those\r
-                               requests.\r
-                               </simpara>\r
-                               </callout>\r
-                               <callout arearefs="CO4-3">\r
-                               <simpara>\r
-                               The <literal>OpenSRF::AppSession-&gt;request()</literal> method accepts a minimum of one\r
-                               argument - the name of the OpenSRF method to which you want to make a request -\r
-                               followed by zero or more arguments to pass to the OpenSRF method as input\r
-                               values. This example passes a string and an integer to the\r
-                               <literal>opensrf.simple-text.substring</literal> method defined by the <literal>opensrf.simple-text</literal>\r
-                               OpenSRF service.\r
-                               </simpara>\r
-                               </callout>\r
-                               <callout arearefs="CO4-4">\r
-                               <simpara>\r
-                               The <literal>gather()</literal> method, called on the result object returned by the\r
-                               <literal>request()</literal> method, iterates over all of the possible results from the result\r
-                               object and returns a single variable.\r
-                               </simpara>\r
-                               </callout>\r
-                               <callout arearefs="CO4-5">\r
-                               <simpara>\r
-                               This <literal>request()</literal> call passes two strings to the <literal>opensrf.simple-text.split</literal>\r
-                               method defined by the <literal>opensrf.simple-text</literal> OpenSRF service and returns (via\r
-                               <literal>gather()</literal>) a reference to an array of results.\r
-                               </simpara>\r
-                               </callout>\r
-                               <callout arearefs="CO4-6">\r
-                               <simpara>\r
-                               The <literal>opensrf.simple-text.split()</literal> method is a streaming method that\r
-                               returns an array of results with one element per <literal>recv()</literal> call on the\r
-                               result object. We could use the <literal>gather()</literal> method to retrieve all of the\r
-                               results in a single array reference, but instead we simply iterate over\r
-                               the result variable until there are no more results to retrieve.\r
-                               </simpara>\r
-                               </callout>\r
-                               <callout arearefs="CO4-7">\r
-                               <simpara>\r
-                               While the <literal>gather()</literal> convenience method returns only the content of the\r
-                               complete set of results for a given request, the <literal>recv()</literal> method returns an\r
-                               OpenSRF result object with <literal>status</literal>, <literal>statusCode</literal>, and <literal>content</literal> fields as\r
-                               we saw in <link linkend="OpenSRFOverHTTP">the HTTP results example</link>.\r
-                               </simpara>\r
-                               </callout>\r
-                               <callout arearefs="CO4-8">\r
-                               <simpara>\r
-                               This <literal>request()</literal> call passes an array to the\r
-                               <literal>opensrf.simple-text.statistics</literal> method defined by the <literal>opensrf.simple-text</literal>\r
-                               OpenSRF service.\r
-                               </simpara>\r
-                               </callout>\r
-                               <callout arearefs="CO4-9">\r
-                               <simpara>\r
-                               The result object returns a hash reference via <literal>gather()</literal>. The hash\r
-                               contains the <literal>length</literal> and <literal>word_count</literal> keys we defined in the method.\r
-                               </simpara>\r
-                               </callout>\r
-                               <callout arearefs="CO4-10">\r
-                               <simpara>\r
-                               The <literal>OpenSRF::AppSession-&gt;disconnect()</literal> method closes the XMPP client\r
-                               connection and cleans up resources associated with the session.\r
-                               </simpara>\r
-                               </callout>\r
-                               </calloutlist>\r
-                       </simplesect>\r
-               </simplesect>\r
-               <simplesect id="_accepting_and_returning_more_interesting_data_types">\r
-                       <title>Accepting and returning more interesting data types</title>\r
-                       <simpara>Of course, the example of accepting a single string and returning a single\r
-                       string is not very interesting. In real life, our applications tend to pass\r
-                       around multiple arguments, including arrays and hashes. Fortunately, OpenSRF\r
-                       makes that easy to deal with; in Perl, for example, returning a reference to\r
-                       the data type does the right thing. In the following example of a method that\r
-                       returns a list, we accept two arguments of type string: the string to be split,\r
-                       and the delimiter that should be used to split the string.</simpara>\r
-                       <formalpara><title>Basic text splitting method</title><para>\r
-<programlisting language="perl" linenumbering="unnumbered">\r
-sub text_split {\r
-    my $self = shift;\r
-    my $conn = shift;\r
-    my $text = shift;\r
-    my $delimiter = shift || ' ';\r
-\r
-    my @split_text = split $delimiter, $text;\r
-    return \@split_text;\r
-}\r
-\r
-__PACKAGE__-&gt;register_method(\r
-    method    =&gt; 'text_split',\r
-    api_name  =&gt; 'opensrf.simple-text.split'\r
-);\r
-</programlisting>\r
-                       </para></formalpara>\r
-                       <simpara>We simply return a reference to the list, and OpenSRF does the rest of the work\r
-                       for us to convert the data into the language-independent format that is then\r
-                       returned to the caller. As a caller of a given method, you must rely on the\r
-                       documentation used to register to determine the data structures - if the developer has\r
-                       added the appropriate documentation.</simpara>\r
-               </simplesect>\r
-               <simplesect id="_accepting_and_returning_evergreen_objects">\r
-                       <title>Accepting and returning Evergreen objects</title>\r
-                       <simpara>OpenSRF is agnostic about objects; its role is to pass JSON back and forth\r
-                       between OpenSRF clients and services, and it allows the specific clients and\r
-                       services to define their own semantics for the JSON structures. On top of that\r
-                       infrastructure, Evergreen offers the fieldmapper: an object-relational mapper\r
-                       that provides a complete definition of all objects, their properties, their\r
-                       relationships to other objects, the permissions required to create, read,\r
-                       update, or delete objects of that type, and the database table or view on which\r
-                       they are based.</simpara>\r
-                       <indexterm><primary>Fieldmapper</primary></indexterm>\r
-                       <simpara>The Evergreen fieldmapper offers a great deal of convenience for working with\r
-                       complex system objects beyond the basic mapping of classes to database\r
-                       schemas. Although the result is passed over the wire as a JSON object\r
-                       containing the indicated fields, fieldmapper-aware clients then turn those\r
-                       JSON objects into native objects with setter / getter methods for each field.</simpara>\r
-                       <simpara>All of this metadata about Evergreen objects is defined in the\r
-                       fieldmapper configuration file (<literal>/openils/conf/fm_IDL.xml</literal>), and access to\r
-                       these classes is provided by the <literal>open-ils.cstore</literal>, <literal>open-ils.pcrud</literal>, and\r
-                       <literal>open-ils.reporter-store</literal> OpenSRF services which parse the fieldmapper\r
-                       configuration file and dynamically register OpenSRF methods for creating,\r
-                       reading, updating, and deleting all of the defined classes.</simpara>\r
-                       <formalpara><title>Example fieldmapper class definition for "Open User Summary"</title><para>\r
-<programlisting language="xml" linenumbering="unnumbered">\r
-&lt;class id="mous" controller="open-ils.cstore open-ils.pcrud"\r
- oils_obj:fieldmapper="money::open_user_summary"\r
- oils_persist:tablename="money.open_usr_summary"\r
- reporter:label="Open User Summary"&gt;                                <co id="CO5-1"/>\r
-    &lt;fields oils_persist:primary="usr" oils_persist:sequence=""&gt; <co id="CO5-2"/> \r
-       &lt;field name="balance_owed" reporter:datatype="money" /&gt;  <co id="CO5-3"/> \r
-       &lt;field name="total_owed" reporter:datatype="money" /&gt;\r
-       &lt;field name="total_paid" reporter:datatype="money" /&gt;\r
-       &lt;field name="usr" reporter:datatype="link"/&gt;\r
-    &lt;/fields&gt;\r
-    &lt;links&gt;\r
-       &lt;link field="usr" reltype="has_a" key="id" map="" class="au"/&gt;<co id="CO5-4"/> \r
-    &lt;/links&gt;\r
-    &lt;permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1"&gt;<co id="CO5-5"/> \r
-       &lt;actions&gt;\r
-           &lt;retrieve permission="VIEW_USER"&gt;<co id="CO5-6"/> \r
-               &lt;context link="usr" field="home_ou"/&gt;<co id="CO5-7"/>\r
-           &lt;/retrieve&gt;\r
-       &lt;/actions&gt;\r
-    &lt;/permacrud&gt;\r
-&lt;/class&gt;\r
-</programlisting>\r
-                       </para></formalpara>\r
-                       <calloutlist>\r
-                       <callout arearefs="CO5-1">\r
-                       <simpara>\r
-                       The <literal>&lt;class&gt;</literal> element defines the class:\r
-                       </simpara>\r
-                       <itemizedlist>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>id</literal> attribute defines the <emphasis>class hint</emphasis> that identifies the class both\r
-                       elsewhere in the fieldmapper configuration file, such as in the value of the\r
-                       <literal>field</literal> attribute of the <literal>&lt;link&gt;</literal> element, and in the JSON object itself when\r
-                       it is instantiated. For example, an "Open User Summary" JSON object would have\r
-                       the top level property of <literal>"__c":"mous"</literal>.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>controller</literal> attribute identifies the services that have direct access\r
-                       to this class. If <literal>open-ils.pcrud</literal> is not listed, for example, then there is\r
-                       no means to directly access members of this class through a public service.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>oils_obj:fieldmapper</literal> attribute defines the name of the Perl\r
-                       fieldmapper class that will be dynamically generated to provide setter and\r
-                       getter methods for instances of the class.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>oils_persist:tablename</literal> attribute identifies the schema name and table\r
-                       name of the database table that stores the data that represents the instances\r
-                       of this class. In this case, the schema is <literal>money</literal> and the table is\r
-                       <literal>open_usr_summary</literal>.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>reporter:label</literal> attribute defines a human-readable name for the class\r
-                       used in the reporting interface to identify the class. These names are defined\r
-                       in English in the fieldmapper configuration file; however, they are extracted\r
-                       so that they can be translated and served in the user&#8217;s language of choice.\r
-                       </simpara>\r
-                       </listitem>\r
-                       </itemizedlist>\r
-                       </callout>\r
-                       <callout arearefs="CO5-2">\r
-                       <simpara>\r
-                       The <literal>&lt;fields&gt;</literal> element lists all of the fields that belong to the object.\r
-                       </simpara>\r
-                       <itemizedlist>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>oils_persist:primary</literal> attribute identifies the field that acts as the\r
-                       primary key for the object; in this case, the field with the name <literal>usr</literal>.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>oils_persist:sequence</literal> attribute identifies the sequence object\r
-                       (if any) in this database provides values for new instances of this class. In\r
-                       this case, the primary key is defined by a field that is linked to a different\r
-                       table, so no sequence is used to populate these instances.\r
-                       </simpara>\r
-                       </listitem>\r
-                       </itemizedlist>\r
-                       </callout>\r
-                       <callout arearefs="CO5-3">\r
-                       <simpara>\r
-                       Each <literal>&lt;field&gt;</literal> element defines a single field with the following attributes:\r
-                       </simpara>\r
-                       <itemizedlist>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>name</literal> attribute identifies the column name of the field in the\r
-                       underlying database table as well as providing a name for the setter / getter\r
-                       method that can be invoked in the JSON or native version of the object.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>reporter:datatype</literal> attribute defines how the reporter should treat\r
-                       the contents of the field for the purposes of querying and display.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>reporter:label</literal> attribute can be used to provide a human-readable name\r
-                       for each field; without it, the reporter falls back to the value of the <literal>name</literal>\r
-                       attribute.\r
-                       </simpara>\r
-                       </listitem>\r
-                       </itemizedlist>\r
-                       </callout>\r
-                       <callout arearefs="CO5-4">\r
-                       <simpara>\r
-                       The <literal>&lt;links&gt;</literal> element contains a set of zero or more <literal>&lt;link&gt;</literal> elements,\r
-                       each of which defines a relationship between the class being described and\r
-                       another class.\r
-                       </simpara>\r
-                       <itemizedlist>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>field</literal> attribute identifies the field named in this class that links\r
-                       to the external class.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>reltype</literal> attribute identifies the kind of relationship between the\r
-                       classes; in the case of <literal>has_a</literal>, each value in the <literal>usr</literal> field is guaranteed\r
-                       to have a corresponding value in the external class.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>key</literal> attribute identifies the name of the field in the external\r
-                       class to which this field links.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The rarely-used <literal>map</literal> attribute identifies a second class to which\r
-                       the external class links; it enables this field to define a direct\r
-                       relationship to an external class with one degree of separation, to\r
-                       avoid having to retrieve all of the linked members of an intermediate\r
-                       class just to retrieve the instances from the actual desired target class.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>class</literal> attribute identifies the external class to which this field\r
-                       links.\r
-                       </simpara>\r
-                       </listitem>\r
-                       </itemizedlist>\r
-                       </callout>\r
-                       <callout arearefs="CO5-5">\r
-                       <simpara>\r
-                       The <literal>&lt;permacrud&gt;</literal> element defines the permissions that must have been\r
-                       granted to a user to operate on instances of this class.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO5-6">\r
-                       <simpara>\r
-                       The <literal>&lt;retrieve&gt;</literal> element is one of four possible children of the\r
-                       <literal>&lt;actions&gt;</literal> element that define the permissions required for each action:\r
-                       create, retrieve, update, and delete.\r
-                       </simpara>\r
-                       <itemizedlist>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>permission</literal> attribute identifies the name of the permission that must\r
-                       have been granted to the user to perform the action.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>contextfield</literal> attribute, if it exists, defines the field in this class\r
-                       that identifies the library within the system for which the user must have\r
-                       prvileges to work. If a user has been granted a given permission, but has not been\r
-                       granted privileges to work at a given library, they can not perform the action\r
-                       at that library.\r
-                       </simpara>\r
-                       </listitem>\r
-                       </itemizedlist>\r
-                       </callout>\r
-                       <callout arearefs="CO5-7">\r
-                       <simpara>\r
-                       The rarely-used <literal>&lt;context&gt;</literal> element identifies a linked field (<literal>link</literal>\r
-                       attribute) in this class which links to an external class that holds the field\r
-                       (<literal>field</literal> attribute) that identifies the library within the system for which the\r
-                       user must have privileges to work.\r
-                       </simpara>\r
-                       </callout>\r
-                       </calloutlist>\r
-                       <simpara>When you retrieve an instance of a class, you can ask for the result to\r
-                       <emphasis>flesh</emphasis> some or all of the linked fields of that class, so that the linked\r
-                       instances are returned embedded directly in your requested instance. In that\r
-                       same request you can ask for the fleshed instances to in turn have their linked\r
-                       fields fleshed. By bundling all of this into a single request and result\r
-                       sequence, you can avoid the network overhead of requiring the client to request\r
-                       the base object, then request each linked object in turn.</simpara>\r
-                       <simpara>You can also iterate over a collection of instances and set the automatically\r
-                       generated <literal>isdeleted</literal>, <literal>isupdated</literal>, or <literal>isnew</literal> properties to indicate that\r
-                       the given instance has been deleted, updated, or created respectively.\r
-                       Evergreen can then act in batch mode over the collection to perform the\r
-                       requested actions on any of the instances that have been flagged for action.</simpara>\r
-               </simplesect>\r
-               <simplesect id="_returning_streaming_results">\r
-                       <title>Returning streaming results</title>\r
-                       <simpara>In the previous implementation of the <literal>opensrf.simple-text.split</literal> method, we\r
-                       returned a reference to the complete array of results. For small values being\r
-                       delivered over the network, this is perfectly acceptable, but for large sets of\r
-                       values this can pose a number of problems for the requesting client. Consider a\r
-                       service that returns a set of bibliographic records in response to a query like\r
-                       "all records edited in the past month"; if the underlying database is\r
-                       relatively active, that could result in thousands of records being returned as\r
-                       a single network request. The client would be forced to block until all of the\r
-                       results are returned, likely resulting in a significant delay, and depending on\r
-                       the implementation, correspondingly large amounts of memory might be consumed\r
-                       as all of the results are read from the network in a single block.</simpara>\r
-                       <simpara>OpenSRF offers a solution to this problem. If the method returns results that\r
-                       can be divided into separate meaningful units, you can register the OpenSRF\r
-                       method as a streaming method and enable the client to loop over the results one\r
-                       unit at a time until the method returns no further results. In addition to\r
-                       registering the method with the provided name, OpenSRF also registers an additional\r
-                       method with <literal>.atomic</literal> appended to the method name. The <literal>.atomic</literal> variant gathers\r
-                       all of the results into a single block to return to the client, giving the caller\r
-                       the ability to choose either streaming or atomic results from a single method\r
-                       definition.</simpara>\r
-                       <simpara>In the following example, the text splitting method has been reimplemented to\r
-                       support streaming; very few changes are required:</simpara>\r
-                       <formalpara><title>Text splitting method - streaming mode</title><para>\r
-<programlisting language="perl" linenumbering="unnumbered">\r
-sub text_split {\r
-    my $self = shift;\r
-    my $conn = shift;\r
-    my $text = shift;\r
-    my $delimiter = shift || ' ';\r
-\r
-    my @split_text = split $delimiter, $text;\r
-    foreach my $string (@split_text) { <co id="CO6-1"/>\r
-       $conn-&gt;respond($string);\r
-    }\r
-    return undef;\r
-}\r
-\r
-__PACKAGE__-&gt;register_method(\r
-    method    =&gt; 'text_split',\r
-    api_name  =&gt; 'opensrf.simple-text.split',\r
-    stream    =&gt; 1<co id="CO6-2"/>\r
-);\r
-</programlisting>\r
-                       </para></formalpara>\r
-                       <calloutlist>\r
-                       <callout arearefs="CO6-1">\r
-                       <simpara>\r
-                       Rather than returning a reference to the array, a streaming method loops\r
-                       over the contents of the array and invokes the <literal>respond()</literal> method of the\r
-                       connection object on each element of the array.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO6-2">\r
-                       <simpara>\r
-                       Registering the method as a streaming method instructs OpenSRF to also\r
-                       register an atomic variant (<literal>opensrf.simple-text.split.atomic</literal>).\r
-                       </simpara>\r
-                       </callout>\r
-                       </calloutlist>\r
-               </simplesect>\r
-               <simplesect id="_error_warning_info_debug">\r
-                       <title>Error! Warning! Info! Debug!</title>\r
-                       <simpara>As hard as it may be to believe, it is true: applications sometimes do not\r
-                       behave in the expected manner, particularly when they are still under\r
-                       development. The service language bindings for OpenSRF include integrated\r
-                       support for logging messages at the levels of ERROR, WARNING, INFO, DEBUG, and\r
-                       the extremely verbose INTERNAL to either a local file or to a syslogger\r
-                       service. The destination of the log files, and the level of verbosity to be\r
-                       logged, is set in the <literal>opensrf_core.xml</literal> configuration file. To add logging to\r
-                       our Perl example, we just have to add the <literal>OpenSRF::Utils::Logger</literal> package to our\r
-                       list of used Perl modules, then invoke the logger at the desired logging level.</simpara>\r
-                       <simpara>You can include many calls to the OpenSRF logger; only those that are higher\r
-                       than your configured logging level will actually hit the log. The following\r
-                       example exercises all of the available logging levels in OpenSRF:</simpara>\r
-<programlisting language="perl" linenumbering="unnumbered">\r
-use OpenSRF::Utils::Logger;\r
-my $logger = OpenSRF::Utils::Logger;\r
-# some code in some function\r
-{\r
-    $logger-&gt;error("Hmm, something bad DEFINITELY happened!");\r
-    $logger-&gt;warn("Hmm, something bad might have happened.");\r
-    $logger-&gt;info("Something happened.");\r
-    $logger-&gt;debug("Something happened; here are some more details.");\r
-    $logger-&gt;internal("Something happened; here are all the gory details.")\r
-}\r
-</programlisting>\r
-                       <simpara>If you call the mythical OpenSRF method containing the preceding OpenSRF logger\r
-                       statements on a system running at the default logging level of INFO, you will\r
-                       only see the INFO, WARN, and ERR messages, as follows:</simpara>\r
-                       <formalpara><title>Results of logging calls at the default level of INFO</title><para>\r
-<screen>\r
-[2010-03-17 22:27:30] opensrf.simple-text [ERR :5681:SimpleText.pm:277:] \r
-[2010-03-17 22:27:30] opensrf.simple-text [WARN:5681:SimpleText.pm:278:] \r
-[2010-03-17 22:27:30] opensrf.simple-text [INFO:5681:SimpleText.pm:279:] \r
-</screen>\r
-                       </para></formalpara>\r
-                       <simpara>If you then increase the the logging level to INTERNAL (5), the logs will\r
-                       contain much more information, as follows:</simpara>\r
-                       <formalpara><title>Results of logging calls at the default level of INTERNAL</title><para>\r
-<screen>\r
-[2010-03-17 22:48:11] opensrf.simple-text [ERR :5934:SimpleText.pm:277:] \r
-[2010-03-17 22:48:11] opensrf.simple-text [WARN:5934:SimpleText.pm:278:] \r
-[2010-03-17 22:48:11] opensrf.simple-text [INFO:5934:SimpleText.pm:279:] \r
-[2010-03-17 22:48:11] opensrf.simple-text [DEBG:5934:SimpleText.pm:280:] \r
-[2010-03-17 22:48:11] opensrf.simple-text [INTL:5934:SimpleText.pm:281:] \r
-[2010-03-17 22:48:11] opensrf.simple-text [ERR :5934:SimpleText.pm:283:] \r
-[2010-03-17 22:48:21] opensrf.simple-text [INTL:5934:Cache.pm:125:] \r
-[2010-03-17 22:48:21] opensrf.simple-text [DEBG:5934:Application.pm:579:] \r
-[2010-03-17 22:48:21] opensrf.simple-text [DEBG:5934:Application.pm:586:] \r
-[2010-03-17 22:48:21] opensrf.simple-text [DEBG:5934:Application.pm:190:] \r
-[2010-03-17 22:48:21] opensrf.simple-text [INTL:5934:AppSession.pm:780:] Calling queue_wait(0)\r
-[2010-03-17 22:48:21] opensrf.simple-text [INTL:5934:AppSession.pm:769:] Resending...0\r
-[2010-03-17 22:48:21] opensrf.simple-text [INTL:5934:AppSession.pm:450:] In send\r
-[2010-03-17 22:48:21] opensrf.simple-text [DEBG:5934:AppSession.pm:506:] \r
-[2010-03-17 22:48:21] opensrf.simple-text [DEBG:5934:AppSession.pm:506:] \r
-...\r
-</screen>\r
-                       </para></formalpara>\r
-                       <simpara>To see everything that is happening in OpenSRF, try leaving your logging level\r
-                       set to INTERNAL for a few minutes - just ensure that you have a lot of free disk\r
-                       space available if you have a moderately busy system!</simpara>\r
-               </simplesect>\r
-               <simplesect id="_caching_results_one_secret_of_scalability">\r
-                       <title>Caching results: one secret of scalability</title>\r
-                       <indexterm><primary>search results</primary><secondary>caching</secondary></indexterm>\r
-                       <simpara>If you have ever used an application that depends on a remote Web service\r
-                       outside of your control&#8201;&#8212;&#8201;say, if you need to retrieve results from a\r
-                       microblogging service&#8201;&#8212;&#8201;you know the pain of latency and dependability (or the\r
-                       lack thereof). To improve the response time for OpenSRF services, you can take\r
-                       advantage of the support offered by the <literal>OpenSRF::Utils::Cache</literal> module for\r
-                       communicating with a local instance or cluster of <literal>memcache</literal> daemons to store\r
-                       and retrieve persistent values. The following example demonstrates caching\r
-                       by sleeping for 10 seconds the first time it receives a given cache key and\r
-                       cannot retrieve a corresponding value from the cache:</simpara>\r
-                       <formalpara><title>Simple caching OpenSRF service</title><para>\r
-<programlisting language="perl" linenumbering="unnumbered">\r
-use OpenSRF::Utils::Cache;<co id="CO7-1"/>\r
-sub test_cache {\r
-    my $self = shift;\r
-    my $conn = shift;\r
-    my $test_key = shift;\r
-    my $cache = OpenSRF::Utils::Cache-&gt;new('global'); <co id="CO7-2"/>\r
-    my $cache_key = "opensrf.simple-text.test_cache.$test_key"; <co id="CO7-3"/>\r
-    my $result = $cache-&gt;get_cache($cache_key) || undef; <co id="CO7-4"/>\r
-    if ($result) {\r
-       $logger-&gt;info("Resolver found a cache hit");\r
-       return $result;\r
-    }\r
-    sleep 10; <co id="CO7-5"/>\r
-    my $cache_timeout = 300; <co id="CO7-6"/>\r
-    $cache-&gt;put_cache($cache_key, "here", $cache_timeout); <co id="CO7-7"/>\r
-    return "There was no cache hit.";\r
-}\r
-</programlisting>\r
-                       </para></formalpara>\r
-                       <calloutlist>\r
-                       <callout arearefs="CO7-1">\r
-                       <simpara>\r
-                       The OpenSRF::Utils::Cache module provides access to the built-in caching\r
-                       support in OpenSRF.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO7-2">\r
-                       <simpara>\r
-                       The constructor for the cache object accepts a single argument to define\r
-                       the cache type for the object. Each cache type can use a separate <literal>memcache</literal>\r
-                       server to keep the caches separated. Most Evergreen services use the <literal>global</literal>\r
-                       cache, while the <literal>anon</literal> cache is used for Web sessions.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO7-3">\r
-                       <simpara>\r
-                       The cache key is simply a string that uniquely identifies the value you\r
-                       want to store or retrieve. This line creates a cache key based on the OpenSRF\r
-                       method name and request input value.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO7-4">\r
-                       <simpara>\r
-                       The <literal>get_cache()</literal> method checks to see if the cache key already exists. If\r
-                       a matching key is found, the service immediately returns the stored value.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO7-5">\r
-                       <simpara>\r
-                       If the cache key does not exist, the code sleeps for 10 seconds to\r
-                       simulate a call to a slow remote Web service or an intensive process.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO7-6">\r
-                       <simpara>\r
-                       The <literal>$cache_timeout</literal> variable represents a value for the lifetime of the\r
-                       cache key in seconds.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO7-7">\r
-                       <simpara>\r
-                       After the code retrieves its value (or, in the case of this example,\r
-                       finishes sleeping), it creates the cache entry by calling the <literal>put_cache()</literal>\r
-                       method. The method accepts three arguments: the cache key, the value to be\r
-                       stored ("here"), and the timeout value in seconds to ensure that we do not\r
-                       return stale data on subsequent calls.\r
-                       </simpara>\r
-                       </callout>\r
-                       </calloutlist>\r
-               </simplesect>\r
-               <simplesect id="_initializing_the_service_and_its_children_child_labour">\r
-                       <title>Initializing the service and its children: child labour</title>\r
-                       <simpara>When an OpenSRF service is started, it looks for a procedure called\r
-                       <literal>initialize()</literal> to set up any global variables shared by all of the children of\r
-                       the service. The <literal>initialize()</literal> procedure is typically used to retrieve\r
-                       configuration settings from the <literal>opensrf.xml</literal> file.</simpara>\r
-                       <simpara>An OpenSRF service spawns one or more children to actually do the work\r
-                       requested by callers of the service. For every child process an OpenSRF service\r
-                       spawns, the child process clones the parent environment and then each child\r
-                       process runs the <literal>child_init()</literal> process (if any) defined in the OpenSRF service\r
-                       to initialize any child-specific settings.</simpara>\r
-                       <simpara>When the OpenSRF service kills a child process, it invokes the <literal>child_exit()</literal>\r
-                       procedure (if any) to clean up any resources associated with the child process.\r
-                       Similarly, when the OpenSRF service is stopped, it calls the <literal>DESTROY()</literal>\r
-                       procedure to clean up any remaining resources.</simpara>\r
-               </simplesect>\r
-               <simplesect id="_retrieving_configuration_settings">\r
-                       <title>Retrieving configuration settings</title>\r
-                       <simpara>The settings for OpenSRF services are maintained in the <literal>opensrf.xml</literal> XML\r
-                       configuration file. The structure of the XML document consists of a root\r
-                       element <literal>&lt;opensrf&gt;</literal> containing two child elements:</simpara>\r
-                       <itemizedlist>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>&lt;default&gt;</literal> element contains an <literal>&lt;apps&gt;</literal> element describing all\r
-                       OpenSRF services running on this system&#8201;&#8212;&#8201;see <xref linkend="serviceRegistration"/> --, as\r
-                       well as any other arbitrary XML descriptions required for global configuration\r
-                       purposes. For example, Evergreen uses this section for email notification and\r
-                       inter-library patron privacy settings.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>&lt;hosts&gt;</literal> element contains one element per host that participates in\r
-                       this OpenSRF system. Each host element must include an <literal>&lt;activeapps&gt;</literal> element\r
-                       that lists all of the services to start on this host when the system starts\r
-                       up. Each host element can optionally override any of the default settings.\r
-                       </simpara>\r
-                       </listitem>\r
-                       </itemizedlist>\r
-                       <simpara>OpenSRF includes a service named <literal>opensrf.settings</literal> to provide distributed\r
-                       cached access to the configuration settings with a simple API:</simpara>\r
-                       <itemizedlist>\r
-                       <listitem>\r
-                       <simpara>\r
-                       <literal>opensrf.settings.default_config.get</literal> accepts zero arguments and returns\r
-                       the complete set of default settings as a JSON document.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       <literal>opensrf.settings.host_config.get</literal> accepts one argument (hostname) and\r
-                       returns the complete set of settings, as customized for that hostname, as a\r
-                       JSON document.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       <literal>opensrf.settings.xpath.get</literal> accepts one argument (an\r
-                       <ulink url="http://www.w3.org/TR/xpath/">XPath</ulink> expression) and returns the portion of\r
-                       the configuration file that matches the expression as a JSON document.\r
-                       </simpara>\r
-                       </listitem>\r
-                       </itemizedlist>\r
-                       <simpara>For example, to determine whether an Evergreen system uses the opt-in\r
-                       support for sharing patron information between libraries, you could either\r
-                       invoke the <literal>opensrf.settings.default_config.get</literal> method and parse the\r
-                       JSON document to determine the value, or invoke the <literal>opensrf.settings.xpath.get</literal>\r
-                       method with the XPath <literal>/opensrf/default/share/user/opt_in</literal> argument to\r
-                       retrieve the value directly.</simpara>\r
-                       <simpara>In practice, OpenSRF includes convenience libraries in all of its client\r
-                       language bindings to simplify access to configuration values. C offers\r
-                       osrfConfig.c, Perl offers <literal>OpenSRF::Utils::SettingsClient</literal>, Java offers\r
-                       <literal>org.opensrf.util.SettingsClient</literal>, and Python offers <literal>osrf.set</literal>. These\r
-                       libraries locally cache the configuration file to avoid network roundtrips for\r
-                       every request and enable the developer to request specific values without\r
-                       having to manually construct XPath expressions.</simpara>\r
-               </simplesect>\r
-       </section>\r
-       <section id="_getting_under_the_covers_with_opensrf">\r
-               <title>OpenSRF Communication Flows</title>\r
-               <indexterm><primary>OpenSRF</primary><secondary>Communication Flows</secondary></indexterm>\r
-               <simpara>Now that you have seen that it truly is easy to create an OpenSRF service, we\r
-               can take a look at what is going on under the covers to make all of this work\r
-               for you.</simpara>\r
-               <simplesect id="_get_on_the_messaging_bus_safely">\r
-                       <title>Get on the messaging bus - safely</title>\r
-                       <simpara>One of the core innovations of OpenSRF was to use the Extensible Messaging and\r
-                       Presence Protocol (XMPP, more colloquially known as Jabber) as the messaging\r
-                       bus that ties OpenSRF services together across servers. XMPP is an "XML\r
-                       protocol for near-real-time messaging, presence, and request-response services"\r
-                       (<ulink url="http://www.ietf.org/rfc/rfc3920.txt">http://www.ietf.org/rfc/rfc3920.txt</ulink>) that OpenSRF relies on to handle most of\r
-                       the complexity of networked communications.  OpenSRF requres an XMPP server\r
-                       that supports multiple domains such as <ulink url="http://www.ejabberd.im/">ejabberd</ulink>.\r
-                       Multiple domain support means that a single server can support XMPP virtual\r
-                       hosts with separate sets of users and access privileges per domain. By\r
-                       routing communications through separate public and private XMPP domains,\r
-                       OpenSRF services gain an additional layer of security.</simpara>\r
-                       <simpara>The <ulink url="http://evergreen-ils.org/dokuwiki/doku.php?id=opensrf:1.2:install">OpenSRF\r
-                       installation documentation</ulink> instructs you to create two separate hostnames\r
-                       (<literal>private.localhost</literal> and <literal>public.localhost</literal>) to use as XMPP domains.  OpenSRF\r
-                       can control access to its services based on the domain of the client and\r
-                       whether a given service allows access from clients on the public domain.  When\r
-                       you start OpenSRF, the first XMPP clients that connect to the XMPP server are\r
-                       the OpenSRF public and private <emphasis>routers</emphasis>. OpenSRF routers maintain a list of\r
-                       available services and connect clients to available services. When an OpenSRF\r
-                       service starts, it establishes a connection to the XMPP server and registers\r
-                       itself with the private router. The OpenSRF configuration contains a list of\r
-                       public OpenSRF services, each of which must also register with the public\r
-                       router.</simpara>\r
-               </simplesect>\r
-               <simplesect id="_opensrf_communication_flows_over_xmpp">\r
-                       <title>OpenSRF communication flows over XMPP</title>\r
-                       <indexterm><primary>XMPP</primary></indexterm>\r
-                       <simpara>In a minimal OpenSRF deployment, two XMPP users named "router" connect to the\r
-                       XMPP server, with one connected to the private XMPP domain and one connected to\r
-                       the public XMPP domain. Similarly, two XMPP users named "opensrf" connect to\r
-                       the XMPP server via the private and public XMPP domains. When an OpenSRF\r
-                       service is started, it uses the "opensrf" XMPP user to advertise its\r
-                       availability with the corresponding router on that XMPP domain; the XMPP server\r
-                       automatically assigns a Jabber ID (<emphasis>JID</emphasis>) based on the client hostname to each\r
-                       service&#8217;s listener process and each connected drone process waiting to carry\r
-                       out requests. When an OpenSRF router receives a request to invoke a method on a\r
-                       given service, it connects the requester to the next available listener in the\r
-                       list of registered listeners for that service.</simpara>\r
-                       <simpara>Services and clients connect to the XMPP server using a single set of XMPP\r
-                       client credentials (for example, <literal>opensrf@private.localhost</literal>), but use XMPP\r
-                       resource identifiers to differentiate themselves in the JID for each\r
-                       connection. For example, the JID for a copy of the <literal>opensrf.simple-text</literal>\r
-                       service with process ID <literal>6285</literal> that has connected to the <literal>private.localhost</literal>\r
-                       domain using the <literal>opensrf</literal> XMPP client credentials could be\r
-                       <literal>opensrf@private.localhost/opensrf.simple-text_drone_at_localhost_6285</literal>.  By\r
-                       convention, the user name for OpenSRF clients is <literal>opensrf</literal>, and the user name\r
-                       for OpenSRF routers is <literal>router</literal>, so the XMPP server for OpenSRF will have four\r
-                       separate users registered:\r
-                         * <literal>opensrf@private.localhost</literal> is an OpenSRF client that connects with these\r
-                       credentials and which can access any OpenSRF service.\r
-                         * <literal>opensrf@public.localhost</literal> is an OpenSRF client that connects with these\r
-                       credentials and which can only access OpenSRF services that have registered\r
-                       with the public router.\r
-                         * <literal>router@private.localhost</literal> is the private OpenSRF router with which all\r
-                       services register.\r
-                         * <literal>router@public.localhost</literal> is the public OpenSRF router with which only\r
-                       services that must be publicly accessible register.</simpara>\r
-                       <simpara>All OpenSRF services automatically register themselves with the private XMPP\r
-                       domain, but only those services that register themselves with the public XMPP\r
-                       domain can be invoked from public OpenSRF clients.  The OpenSRF client and\r
-                       router user names, passwords, and domain names, along with the list of services\r
-                       that should be public, are contained in the <literal>opensrf_core.xml</literal> configuration\r
-                       file.</simpara>\r
-               </simplesect>\r
-               <simplesect id="OpenSRFOverHTTP">\r
-                       <title>OpenSRF communication flows over HTTP</title>\r
-                       <indexterm><primary>HTTP</primary><secondary>translator</secondary></indexterm>\r
-                       <simpara>In some contexts, access to a full XMPP client is not a practical option. For\r
-                       example, while XMPP clients have been implemented in JavaScript, you might\r
-                       be concerned about browser compatibility and processing overhead - or you might\r
-                       want to issue OpenSRF requests from the command line with <literal>curl</literal>. Fortunately,\r
-                       any OpenSRF service registered with the public router is accessible via the\r
-                       OpenSRF HTTP Translator. The OpenSRF HTTP Translator implements the\r
-                       <ulink url="http://www.open-ils.org/dokuwiki/doku.php?id=opensrf_over_http">OpenSRF-over-HTTP\r
-                       proposed specification</ulink> as an Apache module that translates HTTP requests into\r
-                       OpenSRF requests and returns OpenSRF results as HTTP results to the initiating\r
-                       HTTP client.</simpara>\r
-                       <formalpara><title>Issuing an HTTP POST request to an OpenSRF method via the OpenSRF HTTP Translator</title><para>\r
-<programlisting language="bash" linenumbering="unnumbered">\r
-# curl request broken up over multiple lines for legibility\r
-curl -H "X-OpenSRF-service: opensrf.simple-text"<co id="CO8-1"/>\r
-    --data 'osrf-msg=[  \<co id="CO8-2"/>\r
-       {"__c":"osrfMessage","__p":{"threadTrace":0,"locale":"en-CA", <co id="CO8-3"/>\r
-           "type":"REQUEST","payload": {"__c":"osrfMethod","__p": \r
-               {"method":"opensrf.simple-text.reverse","params":["foobar"]}   \r
-           }}                                                                  \r
-       }]'                                                                    \r
-http://localhost/osrf-http-translator <co id="CO8-4"/>\r
-</programlisting>\r
-                       </para></formalpara>\r
-                       <calloutlist>\r
-                       <callout arearefs="CO8-1">\r
-                       <simpara>\r
-                       The <literal>X-OpenSRF-service</literal> header identifies the OpenSRF service of interest.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO8-2">\r
-                       <simpara>\r
-                       The POST request consists of a single parameter, the <literal>osrf-msg</literal> value,\r
-                       which contains a JSON array.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO8-3">\r
-                       <simpara>\r
-                       The first object is an OpenSRF message (<literal>"__c":"osrfMessage"</literal>) with a set of\r
-                       parameters (<literal>"__p":{}</literal>).\r
-                       </simpara>\r
-                       <itemizedlist>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The identifier for the request (<literal>"threadTrace":0</literal>); this value is echoed\r
-                       back in the result.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The message type (<literal>"type":"REQUEST"</literal>).\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The locale for the message; if the OpenSRF method is locale-sensitive, it\r
-                       can check the locale for each OpenSRF request and return different information\r
-                       depending on the locale.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The payload of the message (<literal>"payload":{}</literal>) containing the OpenSRF method\r
-                       request (<literal>"__c":"osrfMethod"</literal>) and its parameters (<literal>"__p:"{}</literal>).\r
-                       </simpara>\r
-                       <itemizedlist>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The method name for the request (<literal>"method":"opensrf.simple-text.reverse"</literal>).\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       A set of JSON parameters to pass to the method (<literal>"params":["foobar"]</literal>); in\r
-                       this case, a single string <literal>"foobar"</literal>.\r
-                       </simpara>\r
-                       </listitem>\r
-                       </itemizedlist>\r
-                       </listitem>\r
-                       </itemizedlist>\r
-                       </callout>\r
-                       <callout arearefs="CO8-4">\r
-                       <simpara>\r
-                       The URL on which the OpenSRF HTTP translator is listening,\r
-                       <literal>/osrf-http-translator</literal> is the default location in the Apache example\r
-                       configuration files shipped with the OpenSRF source, but this is configurable.\r
-                       </simpara>\r
-                       </callout>\r
-                       </calloutlist>\r
-                       <formalpara><title>Results from an HTTP POST request to an OpenSRF method via the OpenSRF HTTP Translator</title><para>\r
-<programlisting language="bash" linenumbering="unnumbered">\r
-# HTTP response broken up over multiple lines for legibility\r
-[{"__c":"osrfMessage","__p":   <co id="CO9-1"/>\r
-    {"threadTrace":0, "payload": <co id="CO9-2"/>\r
-       {"__c":"osrfResult","__p": <co id="CO9-3"/>\r
-           {"status":"OK","content":"raboof","statusCode":200} <co id="CO9-4"/>\r
-       },"type":"RESULT","locale":"en-CA" <co id="CO9-5"/>\r
-    }\r
-},\r
-{"__c":"osrfMessage","__p":   <co id="CO9-6"/>\r
-    {"threadTrace":0,"payload":  <co id="CO9-7"/>\r
-       {"__c":"osrfConnectStatus","__p": <co id="CO9-8"/>\r
-           {"status":"Request Complete","statusCode":205}<co id="CO9-9"/>\r
-       },"type":"STATUS","locale":"en-CA"  <co id="CO9-10"/>\r
-    }\r
-}]\r
-</programlisting>\r
-                       </para></formalpara>\r
-                       <calloutlist>\r
-                       <callout arearefs="CO9-1">\r
-                       <simpara>\r
-                       The OpenSRF HTTP Translator returns an array of JSON objects in its\r
-                       response. Each object in the response is an OpenSRF message\r
-                       (<literal>"__c":"osrfMessage"</literal>) with a collection of response parameters (<literal>"__p":</literal>).\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO9-2">\r
-                       <simpara>\r
-                       The OpenSRF message identifier (<literal>"threadTrace":0</literal>) confirms that this\r
-                       message is in response to the request matching the same identifier.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO9-3">\r
-                       <simpara>\r
-                       The message includes a payload JSON object (<literal>"payload":</literal>) with an OpenSRF\r
-                       result for the request (<literal>"__c":"osrfResult"</literal>).\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO9-4">\r
-                       <simpara>\r
-                       The result includes a status indicator string (<literal>"status":"OK"</literal>), the content\r
-                       of the result response - in this case, a single string "raboof"\r
-                       (<literal>"content":"raboof"</literal>) - and an integer status code for the request\r
-                       (<literal>"statusCode":200</literal>).\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO9-5">\r
-                       <simpara>\r
-                       The message also includes the message type (<literal>"type":"RESULT"</literal>) and the\r
-                       message locale (<literal>"locale":"en-CA"</literal>).\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO9-6">\r
-                       <simpara>\r
-                       The second message in the set of results from the response.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO9-7">\r
-                       <simpara>\r
-                       Again, the message identifier confirms that this message is in response to\r
-                       a particular request.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO9-8">\r
-                       <simpara>\r
-                       The payload of the message denotes that this message is an\r
-                       OpenSRF connection status message (<literal>"__c":"osrfConnectStatus"</literal>), with some\r
-                       information about the particular OpenSRF connection that was used for this\r
-                       request.\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO9-9">\r
-                       <simpara>\r
-                       The response parameters for an OpenSRF connection status message include a\r
-                       verbose status (<literal>"status":"Request Complete"</literal>) and an integer status code for\r
-                       the connection status (`"statusCode":205).\r
-                       </simpara>\r
-                       </callout>\r
-                       <callout arearefs="CO9-10">\r
-                       <simpara>\r
-                       The message also includes the message type (<literal>"type":"RESULT"</literal>) and the\r
-                       message locale (<literal>"locale":"en-CA"</literal>).\r
-                       </simpara>\r
-                       </callout>\r
-                       </calloutlist>\r
-                       <tip><simpara>Before adding a new public OpenSRF service, ensure that it does\r
-                       not introduce privilege escalation or unchecked access to data. For example,\r
-                       the Evergreen <literal>open-ils.cstore</literal> private service is an object-relational mapper\r
-                       that provides read and write access to the entire Evergreen database, so it\r
-                       would be catastrophic to expose that service publicly. In comparison, the\r
-                       Evergreen <literal>open-ils.pcrud</literal> public service offers the same functionality as\r
-                       <literal>open-ils.cstore</literal> to any connected HTTP client or OpenSRF client, but the\r
-                       additional authentication and authorization layer in <literal>open-ils.pcrud</literal> prevents\r
-                       unchecked access to Evergreen&#8217;s data.</simpara></tip>\r
-               </simplesect>\r
-               <simplesect id="_stateless_and_stateful_connections">\r
-                       <title>Stateless and stateful connections</title>\r
-                       <simpara>OpenSRF supports both <emphasis>stateless</emphasis> and <emphasis>stateful</emphasis> connections.  When an OpenSRF\r
-                       client issues a <literal>REQUEST</literal> message in a <emphasis>stateless</emphasis> connection, the router\r
-                       forwards the request to the next available service and the service returns the\r
-                       result directly to the client.</simpara>\r
-                       \r
-                       <simpara>When an OpenSRF client issues a <literal>CONNECT</literal> message to create a <emphasis>stateful</emphasis> conection, the\r
-                       router returns the Jabber ID of the next available service to the client so\r
-                       that the client can issue one or more <literal>REQUEST</literal> message directly to that\r
-                       particular service and the service will return corresponding <literal>RESULT</literal> messages\r
-                       directly to the client. Until the client issues a <literal>DISCONNECT</literal> message, that\r
-                       particular service is only available to the requesting client. Stateful connections\r
-                       are useful for clients that need to make many requests from a particular service,\r
-                       as it avoids the intermediary step of contacting the router for each request, as\r
-                       well as for operations that require a controlled sequence of commands, such as a\r
-                       set of database INSERT, UPDATE, and DELETE statements within a transaction.</simpara>\r
-                       \r
-               </simplesect>\r
-               <simplesect id="_message_body_format">\r
-                       <title>Message body format</title>\r
-                       <simpara>OpenSRF was an early adopter of JavaScript Object Notation (JSON). While XMPP\r
-                       is an XML protocol, the Evergreen developers recognized that the compactness of\r
-                       the JSON format offered a significant reduction in bandwidth for the volume of\r
-                       messages that would be generated in an application of that size. In addition,\r
-                       the ability of languages such as JavaScript, Perl, and Python to generate\r
-                       native objects with minimal parsing offered an attractive advantage over\r
-                       invoking an XML parser for every message. Instead, the body of the XMPP message\r
-                       is a simple JSON structure. For a simple request, like the following example\r
-                       that simply reverses a string, it looks like a significant overhead: but we get\r
-                       the advantages of locale support and tracing the request from the requester\r
-                       through the listener and responder (drone).</simpara>\r
-                       <formalpara><title>A request for opensrf.simple-text.reverse("foobar"):</title><para>\r
-<programlisting language="xml" linenumbering="unnumbered">\r
-&lt;message from='router@private.localhost/opensrf.simple-text'\r
-  to='opensrf@private.localhost/opensrf.simple-text_listener_at_localhost_6275'\r
-  router_from='opensrf@private.localhost/_karmic_126678.3719_6288'\r
-  router_to='' router_class='' router_command='' osrf_xid=''\r
-&gt;\r
-  &lt;thread&gt;1266781414.366573.12667814146288&lt;/thread&gt;\r
-  &lt;body&gt;\r
-[\r
-  {"__c":"osrfMessage","__p":\r
-    {"threadTrace":"1","locale":"en-US","type":"REQUEST","payload":\r
-      {"__c":"osrfMethod","__p":\r
-       {"method":"opensrf.simple-text.reverse","params":["foobar"]}\r
-      }\r
-    }\r
-  }\r
-]\r
-  &lt;/body&gt;\r
-&lt;/message&gt;\r
-</programlisting>\r
-                       </para></formalpara>\r
-                       <formalpara><title>A response from opensrf.simple-text.reverse("foobar")</title><para>\r
-<programlisting language="xml" linenumbering="unnumbered">\r
-&lt;message from='opensrf@private.localhost/opensrf.simple-text_drone_at_localhost_6285'\r
-  to='opensrf@private.localhost/_karmic_126678.3719_6288'\r
-  router_command='' router_class='' osrf_xid=''\r
-&gt;\r
-  &lt;thread&gt;1266781414.366573.12667814146288&lt;/thread&gt;\r
-  &lt;body&gt;\r
-[\r
-  {"__c":"osrfMessage","__p":\r
-    {"threadTrace":"1","payload":\r
-      {"__c":"osrfResult","__p":\r
-       {"status":"OK","content":"raboof","statusCode":200}\r
-      } ,"type":"RESULT","locale":"en-US"}\r
-  },\r
-  {"__c":"osrfMessage","__p":\r
-    {"threadTrace":"1","payload":\r
-      {"__c":"osrfConnectStatus","__p":\r
-       {"status":"Request Complete","statusCode":205}\r
-      },"type":"STATUS","locale":"en-US"}\r
-  }\r
-]\r
-  &lt;/body&gt;\r
-&lt;/message&gt;\r
-</programlisting>\r
-                       </para></formalpara>\r
-                       <simpara>The content of the <literal>&lt;body&gt;</literal> element of the OpenSRF request and result should\r
-                       look familiar; they match the structure of the <link linkend="OpenSRFOverHTTP">OpenSRF over HTTP examples</link> that we previously dissected.</simpara>\r
-               </simplesect>\r
-               <simplesect id="_registering_opensrf_methods_in_depth">\r
-                       <title>Registering OpenSRF methods in depth</title>\r
-                       <simpara>Let&#8217;s explore the call to <literal>__PACKAGE__-&gt;register_method()</literal>; most of the members\r
-                       of the hash are optional, and for the sake of brevity we omitted them in the\r
-                       previous example. As we have seen in the results of the <link linkend="opensrfIntrospection">introspection call</link>, a\r
-                       verbose registration method call is recommended to better enable the internal\r
-                       documentation. Here is the complete set of members that you should pass to\r
-                       <literal>__PACKAGE__-&gt;register_method()</literal>:</simpara>\r
-                       <itemizedlist>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>method</literal> member specifies the name of the procedure in this module that is being registered as an OpenSRF method.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>api_name</literal> member specifies the invocable name of the OpenSRF method; by convention, the OpenSRF service name is used as the prefix.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The optional <literal>api_level</literal> member can be used for versioning the methods to allow the use of a deprecated API, but in practical use is always 1.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The optional <literal>argc</literal> member specifies the minimal number of arguments that the method expects.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The optional <literal>stream</literal> member, if set to any value, specifies that the method supports returning multiple values from a single call to \r
-                       subsequent requests. OpenSRF automatically creates a corresponding method with ".atomic" appended to its name that returns the complete set of results in a \r
-                       single request. Streaming methods are useful if you are returning hundreds of records and want to act on the results as they return.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The optional <literal>signature</literal> member is a hash that describes the method&#8217;s purpose, arguments, and return value.\r
-                       </simpara>\r
-                       <itemizedlist>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>desc</literal> member of the <literal>signature</literal> hash describes the method&#8217;s purpose.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>params</literal> member of the <literal>signature</literal> hash is an array of hashes in which each array element describes the corresponding method \r
-                       argument in order.\r
-                       </simpara>\r
-                       <itemizedlist>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>name</literal> member of the argument hash specifies the name of the argument.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>desc</literal> member of the argument hash describes the argument&#8217;s purpose.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>type</literal> member of the argument hash specifies the data type of the argument: for example, string, integer, boolean, number, array, or hash.\r
-                       </simpara>\r
-                       </listitem>\r
-                       </itemizedlist>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>return</literal> member of the <literal>signature</literal> hash is a hash that describes the return value of the method.\r
-                       </simpara>\r
-                       <itemizedlist>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>desc</literal> member of the <literal>return</literal> hash describes the return value.\r
-                       </simpara>\r
-                       </listitem>\r
-                       <listitem>\r
-                       <simpara>\r
-                       The <literal>type</literal> member of the <literal>return</literal> hash specifies the data type of the return value: for example, string, integer, boolean, number, \r
-                       array, or hash.\r
-                       </simpara>\r
-                       </listitem>\r
-                       </itemizedlist>\r
-                       </listitem>\r
-                       </itemizedlist>\r
-                       </listitem>\r
-                       </itemizedlist>\r
-               </simplesect>\r
-       </section>\r
-       <section id="_evergreen_specific_opensrf_services">\r
-               <title>Evergreen-specific OpenSRF services</title>\r
-               <simpara>Evergreen is currently the primary showcase for the use of OpenSRF as an\r
-               application architecture. Evergreen 1.6.1 includes the following\r
-               set of OpenSRF services:</simpara>\r
-               <itemizedlist>\r
-               <listitem>\r
-               <simpara>\r
-               The <systemitem class="service">open-ils.actor</systemitem> service supports common tasks for working with user\r
-                    accounts and libraries.\r
-               </simpara>\r
-               </listitem>\r
-               <listitem>\r
-               <simpara>\r
-               The <systemitem class="service">open-ils.auth</systemitem> service supports authentication of Evergreen users.\r
-               </simpara>\r
-               </listitem>\r
-               <listitem>\r
-               <simpara>\r
-               The <systemitem class="service">open-ils.booking</systemitem> service supports the management of reservations\r
-                   for bookable items.\r
-               </simpara>\r
-               </listitem>\r
-               <listitem>\r
-               <simpara>\r
-               The <systemitem class="service">open-ils.cat</systemitem> service supports common cataloging tasks, such as\r
-                    creating, modifying, and merging bibliographic and authority records.\r
-               </simpara>\r
-               </listitem>\r
-               <listitem>\r
-               <simpara>\r
-               The <systemitem class="service">open-ils.circ</systemitem> service supports circulation tasks such as checking\r
-                   out items and calculating due dates.\r
-               </simpara>\r
-               </listitem>\r
-               <listitem>\r
-               <simpara>\r
-               The <systemitem class="service">open-ils.collections</systemitem> service supports tasks that assist collections\r
-                   agencies in contacting users with outstanding fines above a certain\r
-                   threshold.\r
-               </simpara>\r
-               </listitem>\r
-               <listitem>\r
-               <simpara>\r
-               The <systemitem class="service">open-ils.cstore</systemitem> private service supports unrestricted access to\r
-                   Evergreen fieldmapper objects.\r
-               </simpara>\r
-               </listitem>\r
-               <listitem>\r
-               <simpara>\r
-               The <systemitem class="service">open-ils.ingest</systemitem> private service supports tasks for importing\r
-                   data such as bibliographic and authority records.\r
-               </simpara>\r
-               </listitem>\r
-               <listitem>\r
-               <simpara>\r
-               The <systemitem class="service">open-ils.pcrud</systemitem> service supports permission-based access to Evergreen\r
-                   fieldmapper objects.\r
-               </simpara>\r
-               </listitem>\r
-               <listitem>\r
-               <simpara>\r
-               The <systemitem class="service">open-ils.penalty</systemitem> penalty service supports the calculation of\r
-                   penalties for users, such as being blocked from further borrowing, for\r
-                   conditions such as having too many items checked out or too many unpaid\r
-                   fines.\r
-               </simpara>\r
-               </listitem>\r
-               <listitem>\r
-               <simpara>\r
-               The <systemitem class="service">open-ils.reporter</systemitem> service supports the creation and scheduling of\r
-                   reports.\r
-               </simpara>\r
-               </listitem>\r
-               <listitem>\r
-               <simpara>\r
-               The <systemitem class="service">open-ils.reporter-store</systemitem> private service supports access to Evergreen\r
-                   fieldmapper objects for the reporting service.\r
-               </simpara>\r
-               </listitem>\r
-               <listitem>\r
-               <simpara>\r
-               The <systemitem class="service">open-ils.search</systemitem> service supports searching across bibliographic\r
-                   records, authority records, serial records, Z39.50 sources, and ZIP codes.\r
-               </simpara>\r
-               </listitem>\r
-               <listitem>\r
-               <simpara>\r
-               The <systemitem class="service">open-ils.storage</systemitem> private service supports a deprecated method of\r
-                   providing access to Evergreen fieldmapper objects. Implemented in Perl,\r
-                   this service has largely been replaced by the much faster C-based\r
-                   <literal>open-ils.cstore</literal> service.\r
-               </simpara>\r
-               </listitem>\r
-               <listitem>\r
-               <simpara>\r
-               The <systemitem class="service">open-ils.supercat</systemitem> service supports transforms of MARC records into\r
-                   other formats, such as MODS, as well as providing Atom and RSS feeds and\r
-                   SRU access.\r
-               </simpara>\r
-               </listitem>\r
-               <listitem>\r
-               <simpara>\r
-               The <systemitem class="service">open-ils.trigger</systemitem> private service supports event-based triggers for\r
-                   actions such as overdue and holds available notification emails.\r
-               </simpara>\r
-               </listitem>\r
-               <listitem>\r
-               <simpara>\r
-               The <systemitem class="service">open-ils.vandelay</systemitem> service supports the import and export of batches of\r
-                   bibliographic and authority records.\r
-               </simpara>\r
-               </listitem>\r
-               </itemizedlist>\r
-               <simpara>Of some interest is that the <systemitem class="service">open-ils.reporter-store</systemitem> and <systemitem class="service">open-ils.cstore</systemitem>\r
-               services have identical implementations. Surfacing them as separate services\r
-               enables a deployer of Evergreen to ensure that the reporting service does not\r
-               interfere with the performance-critical <systemitem class="service">open-ils.cstore</systemitem> service. One can also\r
-               direct the reporting service to a read-only database replica to, again, avoid\r
-               interference with <systemitem class="service">open-ils.cstore</systemitem> which must write to the master database.</simpara>\r
-               <simpara>There are only a few significant services that are not built on OpenSRF in\r
-               Evergreen 1.6.0, such as the SIP and Z39.50 servers. These services implement\r
-               different protocols and build on existing daemon architectures (Simple2ZOOM\r
-               for Z39.50), but still rely on the other OpenSRF services to provide access\r
-               to the Evergreen data. The non-OpenSRF services are reasonably self-contained\r
-               and can be deployed on different servers to deliver the same sort of deployment\r
-               flexibility as OpenSRF services, but have the disadvantage of not being\r
-               integrated into the same configuration and control infrastructure as the\r
-               OpenSRF services.</simpara>\r
-       </section>\r
-</chapter>\r