Add file for 1.6 branch.
[Evergreen-DocBook.git] / development / OpenSRF_intro.xml
diff --git a/development/OpenSRF_intro.xml b/development/OpenSRF_intro.xml
new file mode 100644 (file)
index 0000000..f345997
--- /dev/null
@@ -0,0 +1,1738 @@
+<?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