Pointers for Further Development

The previous two chapters have demonstrated
how Jabber can be used to build applications and solutions in many
functional areas. They expanded upon and indeed went beyond the theme of
instant messaging (IM) to employ the fundamental features of contextual
messaging, presence, and request/response sequences, in a wide range of
scenarios.

While these scenarios have in some ways been inward looking, they are
natural progressions that originated inside the IM world and matured
into applications and solutions that retain much of the messaging
flavor. Let's consider what else Jabber has to offer as a messaging and
routing mechanism.

This chapter explores some "outward looking" scenarios, to give you
pointers and ideas for the future. With Demo::JBook, we consider
the possibility of "Jabber without Jabber"—in other words, using Jabber
as an infrastructure, in this case as a data store, without focus on
any particular Jabber client or IM functionality. We also explore how
Jabber is the perfect transport partner for procedure calls formalized
in XML: JabberRPCRequester and JabberRPCResponder are scripts
that exchange method calls and responses encoded in XML-RPC.

Using Jabber as a conduit to foreign systems is also a theme of this
chapter. With ldapr, we build a script that reflects the hierarchy
and contents of an LDAP data store, allowing that store to be navigated
from a Jabber client. Finally, we look to the business world, employing
Jabber in a tiny but crucial role as a conduit between SAP systems and
their users.

A Simple Jabber-Based Address Book

With the availability of many
different off-the-shelf Jabber clients and the use of these clients as
generic tools to interact with diverse Jabber-based services, it's easy
to lose sight of the fact that Jabber can also be used to contribute to
infrastructure solutions. That is, applications and utilities can be
built using the Jabber protocols, in conjunction with Jabber
server-based services, without the need for a Jabber client.

By way of illustration, let's build a simple two-level address book
using Jabber services. We'll call it Demo::JBook. We'll use this
address book to look up details of our friends and colleagues while
we're on the move. The ideal platform for this is going to be a web
browser, in that it's accessible from personal workstations, airport web
consoles, cybercafés, and personal digital assistants (PDAs) that offer
access to the Internet.

The point of this illustration is not to show that there's a single
solution to the problem of disconnected, incompatible, and
unsynchronized directory information (because, despite any answers that
you may get to the contrary—no such solution exists). Instead, the goal
is to show that it's possible to make use of Jabber services and get to
information stored and managed by those services without having to use a
Jabber client.

Using the JUD and vCards

The two levels in our address book are
going to reflect two distinct (but related) mechanisms in Jabber. We're
going to base our address book on the Jabber User Directory (JUD)
component and supply further information, in a "drill-down" action,
using vCards.

The JUD

Our address book will act as a query frontend for a user directory in

the form of a JUD. It doesn't matter which JUD we use; obviously,

that depends on how the application is to be deployed. On one hand, it

Users interact with the JUD to manage their information, using IQ
elements qualified by the jabber:iq:register namespace. User
vCard information is also stored at the server side, and users manage
the vCard contents using IQ elements qualified by the
vcard-temp namespace. Again, with the default configuration,
the vCard information, stored along with the rest of the user data
relevant to the Jabber server in user-specific spool files, will be held
in the Jabber server's spool directory in files called
[hostname]/[user].xml.

What Demo::JBook Will Do

The JUD can be queried using a normal
Jabber client. In Figure 10-1, we can see the search form of Jabber
Instant Messenger (JIM).

</code> The search fields are not fixed;
instead, they're dynamically generated, according to the result of an
initial IQ-get in the jabber:iq:search namespace. Just as an
IQ-get in the jabber:iq:register namespace (as illustrated in
Section 8.3) is used for registering users, the
jabber:iq:search namespace is is used for search requests. This
is illustrated in Example 10-1, where an IQ-get is sent to the JUD at
jud.gnu.mine.nu.

RECV: <iq from='jud.gnu.mine.nu' id='9138'
to='qmacro@jabber.org/study' type='result'> <query
xmlns='jabber:iq:search'> <first/> <last/>
<instructions> Fill in a field to search for any matching Jabber
users. </instructions> </query> </iq>
In response to the IQ-get request, the JUD sends back a list of fields
with which the directory can be searched, along with some simple
instructions.

The actual search follows the same registration pattern (using the
jabber:iq:register namespace) that we saw in Section 8.3. After
receiving a list of possible search fields, a search request is made
with an IQ-set, as shown in Example 10-2. The results, returned in an
IQ-result from the JUD, are listed with each entry contained in an
<item/> tag. The search results are shown in Figure 10-2.

[[image:title=Searching the JUD: the search
results|image=0596002025-jab_1002.png</code>

Searching a JUD

The first level that we'll build into
Demo::JBook is the ability to query a JUD. The address book is to be
browser-based, so we'll generate HTML on the fly according to the search
fields we receive in response to our IQ-get. It should look something
like that shown in Figure 10-3.

</code>
The items returned in the search results don't really provide much
information:

<item jid='joseph@gnu.mine.nu'>
<nick>joseph</nick> <first>Joseph</first>
<email>joseph@pipetree.com</email>
<last>Adams</last> </item>
Here, we have the user's first and last names, his nickname, and his
email address. One of the functions of the JUD is to determine which
fields are storable.

{{Sidebar|JUD FieldsThe fields in a JUD that are used to store the
directory data are determined either by a hardcoded list in the JUD
source itself or a configurable list that's stored and maintained
separate from the JUD code. The list fields allowed for searching a JUD
may or may not be used for storing the information; it may be a subset
(it makes no sense, of course, for it to be a superset). For example, if
the fields used to store information in a JUD are:

<name/> <email/> <first/> <last/>
<nick/> <text/> it may be that the fields available for
searching may be just:

<first/> <last/>
as shown in Example 10-1.

In case you're wondering how to discover the fields that can be used to
populate the information in the JUD, an IQ-get can be used in the
jabber:iq:register namespace to qualify a registration
conversation with the JUD, which is where the information is
"registered," or stored. This is shown in Example 10-3.

RECV: <iq from='jud.gnu.mine.nu' to='qmacro@jabber.com/study'
type='result'> <query xmlns='jabber:iq:register'> <nick/>
<first/> <email/> <last/> <text/> <name/>
<instructions> Fill in all of the fields to add yourself to the
JUD. </instructions> </query> </iq>
We can see in Figure 10-4 how the results will typically be rendered.

Retrieving vCard information

As well as registering with a
JUD, it's possible that a user has maintained more information about
himself—in his vCard. Depending on the Jabber client used, various user
information can be stored in a personal vCard, which is stored on the
server side. In Figure 10-5, we see the JIM client's vCard maintenance
window, titled User Profile.

The result of entering information and clicking the OK button in Figure
10-5 can be seen in Example 10-4, where an IQ-set in the
vcard-temp namespace is made to store the information (some of
the vCard tags have been omitted to keep the example short). Notice how
no to attribute is specified in the IQ-set and how the result
appears to come from the sender (qmacro@jabber.com/study). The
storage of personal—user-specific—vCard information is a function of the
Jabber Session Manager (JSM), which is where the <iq/>
element will be routed automatically, as it is coming in over a client
connection (defined by the jabber:client stream-level
namespace). This is further discussed in Section 5.4.3.1.

RECV: <iq type='result' from='qmacro@jabber.com/study'
to='qmacro@jabber.com/study'/>
As well as being storable, the information in a personal vCard is also
retrievable by anyone, anytime. The idea of a personal vCard is that the
information it contains is permanently available. Because the vCard data
is stored server side, it can be retrieved anytime the user is online.

The key (literally and metaphorically) of each of the search result
items returned is a JID. We can see this in Example 10-2, where each
<item/> tag has a jid attribute. You won't be
surprised to know that the key to accessing someone's vCard is his JID
too. So we have a great way for Demo::JBook to jump from level 1,
which displays JUD search results, to level 2 by retrieving and
displaying a vCard via the JID. The results of jumping from a JUD result
entry to a vCard display for the selected user, via the JUD, can be seen
in Figure 10-6.

Using Demo::JBook as an Apache Handler

The Demo::JBook
application is going to exist as an Apache mod_perl handler, that
is, we'll use the power of Perl's integration into the Apache web server
to write an Apache module in Perl. You can find out more about
mod_perl at http://perl.apache.org.

Being a mod_perl module, Demo::JBook exists in the form of a
Perl module and is configured in Apache to service calls to
http://[hostname]/jbook. The configuration can be
comfortably placed in the main Apache configuration file, httpd.conf,
or, as is common in mod_perl installations, in an extra file usually
called perl.conf, which is linked to httpd.conf as follows:

<IfModule mod_perl.c> Include conf/perl.conf
</IfModule>
The configuration for Demo::JBook is placed in the perl.conf file,
as shown in Example 10-5.

Configuring the module as a handler in Apache

require conf/startup.pl

...

<Location /jbook> SetHandler perl-script PerlHandler Demo::JBook
</Location>
The PerlHandler directive refers to the Demo::JBook module,
which is the JBook.pm file that exists in the Demo/ directory. The
module will be invoked to handle calls to the relative URL /jbook.
You can add the location of the Demo::JBook module to mod_perl's
list of directories in the BEGIN section of the startup.pl
script, in the conf/ directory, like this:

Taking Demo::JBook Step by Step

Now that we've got a hold on the
scope and scale of Demo::JBook, let's take the script apart—step by
step—to see how it works.

Declarations

Because Demo::JBook is an Apache handler, it
exists as a Perl module—hence the package declaration at the
top of the file:

package Demo::JBook;

use strict;

use Jabber::Connection; use Jabber::NodeFactory; use Jabber::NS qw(:iq

misc);

use constant SERVER => 'gnu.mine.nu'; use constant USER =>
'jbook'; use constant PASS => 'pass'; use constant RESOURCE =>
'jbook'; use constant JUD => 'users.jabber.org';
This code exists within the Demo::JBook package that was declared as
the handler for the http://[hostname]/jbook
location, shown in Example 10-5. We're going to make full use of the
Jabber::Connection library and bring in all three of its
modules for managing the Jabber server connection, for dispatching
elements that arrive (Jabber::Connection), for building and
manipulating elements (Jabber::NodeFactory), and using common
Jabber programming constants (Jabber::NS). In the case of
Jabber::NS, we need only a few namespaces to manage our JUD and
vCard queries, so the :iq and :misc tags will be used
to refer a collection of constants in Jabber::NS.

The constants SERVER, USER, PASS, and
RESOURCE define the connection to the Jabber server. This
connection doesn't have to be made to the JUD that will be queried by
Demo::JBook. By way of illustration, we have jabber.org's JUD
(users.jabber.org) specified as the value for the JUD
constant. For the purpose of this example, this will be the JUD that
Demo::JBook will query.

You can use the reguser script, described in Section 7.4, to create
the Demo::JBook user. See Section 8.2.1.1 for an example of how this
can be done.

General handler preparation

Following the script's
declarations, it's time to define the handler function that Apache will
call to handle incoming requests to the
http://[hostname]/jbook location. The name of the
handler must be handler():

The mod_perl mechanism hands the handler() function an
argument that is stored in a variable called $r. This is the
HyperText Transfer Protocol (HTTP) request that has been made, which
is handled by the function.

Calling the args() method on our request object gives us a list
of arguments. These arguments follow the question mark in a typical HTTP
GET request. In Figure 10-3, the URL in the Location bar is
http://www.pipetree.com/jbook. In this URL, there is neither a question
mark nor any arguments. The assignment to @a here:

my @a = $r->args;
would leave @a empty.

{{Note|The first part of the URL containing the hostname is truncated by
the size of the browser window.

</code>
However, if the URL in the Location bar (shown in Figure 10-4) contained
question marks and/or arguments, such as:

dj@gnu.mine.nu The content of @a defines which stage
of the script takes the appropriate action. Queries will be generated
in the form of IQ-gets and IQ-sets to retrieve information from the JUD
as well as to retrieve personal vCards. To do this, we need to create an
instance of a node factory, so we can build elements:

my $nf = Jabber::NodeFactory->new;
After retrieving the request arguments and creating a node factory
instance, it's time to generate some HTML that will be common to all of
Demo::JBook's features. The print() method will be used on
the request object $r to send a response back to the requesting
web browser:

$r->print("<html><head><title>JBook</title>&
lt;/head><body>"); $r->print("<h1><a
href="/jbook">JBook</a></h1>");
Next, we need to create a connection to the Jabber server for each
request:

It will be much more efficient to create a persistent connection that
could be used to serve further requests. One way to do this is to fork a
daemon that connects to the Jabber server, acting as a proxy for this
script. Rather than make direct requests to the Jabber server,
Demo::JBook is used to send the IQ elements to the daemon, which in
turn uses its persistent Jabber connection to make queries on
Demo::JBook's behalf. A setup like this is shown in Figure 10-7.

</code>

State 1: Build the JUD query form

Based upon how many
arguments we have in the @a array, as described earlier, we can
build the response, which will represent one of three states:

JUD query form

JUD query results

vCard display If Demo::JBook is called without arguments (i.e.,

http://[hostname]/jbook), which we test for like
this:

if (scalar @a == 0) {
then we want to build and present the JUD search form. This form—the
fields that the form consists of—will be specific to the particular JUD
that will be searched.

We construct an IQ-get to send to the JUD to ask for a list of search
fields and instructions. What we're looking for is something like the
IQ-get shown in Example 10-1, like this:

SEND: <iq type="get" to="users.jabber.org"> <query
xmlns="jabber:iq:search"/> </iq>
To construct this, we start with a new node (element) created by
using the node factory:

my $iq = $nf->newNode('iq'); $iq->attr('to', JUD);
$iq->attr('type', IQ_GET); $iq->insertTag('query', NS_SEARCH);
In previous recipes, we've called methods in the Jabber libraries
(Jabberpy, JabberBeans, Net::Jabber, and
Jabber::Connection) to send an element to the Jabber server.
Typically, such a method is called a send() method. Here, we
don't use a send() method. Instead, we use
Jabber::Connection's ask(). Like
Net::Jabber's SendAndReceiveWithID() method, and
Jabberpy's method of the same name, ask() not only
sends the element to the Jabber server, it waits for a reply.

A reply—in Jabber terms—is a response in the form of an element
that comes back along the stream, with a
matchingidattribute. In this case, we're making an IQ-get
and are expecting a response back from the recipient of that IQ-get. One
way of expecting and handling the response would be to use a predefined
callback specified for <iq/> elements and send our IQ-get
with the send() method. However, this means that the script
receives the response in an element callback that's somewhat independent
of our call to send(). As well as catching the response in the
callback, we also need some way of matching it up with the original
request. Not to mention getting back on track with the flow of execution
that represented the logical sequence of events that we were in the
middle of following when we called send().

There's an easier way, if we want to make a request and wait for the
response to that request before continuing on in the script. This way
makes use of the ask() method.

What the ask() method does is avoid the need to catch responses
in callbacks and match them up with their originating requests. It
send()s the element to the Jabber server and blocks until an
element with a matching id attribute value is received. Other
elements that might be received while waiting for the response are duly
dispatched as normal. It's not as if elements get queued up if the
response takes a moment or two to arrive. When the matching element
arrives on the stream, it is passed directly back to the caller of the
ask() method, in the same form as if it had been handed to a
callback—in object form, as an instance of a
Jabber::NodeFactory::Node object, in this case.

my $result = $c->ask($iq);
So here, $result receives the response to the
<iq/> element just sent. This response will look
something like this:

RECV: <iq from='users.jabber.org' id='43'
to='jbook@gnu.mine.nu/jbook' type='result'> <query
xmlns='jabber:iq:search'> <nick/> <first/> <email/>
<last/> <instructions> Fill in a field to search for any
matching Jabber users. </instructions> </query> </iq>
It's addressed to the JID that the script is using,
jbook@gnu.mine.nu/jbook. But hold on—there's something that
doesn't look quite right here, that is, when compared to the example of
the IQ-get we just sent. Indeed—there's an id attribute in the
response. We didn't specify one in the Perl code that built the
<iq/> element. The ask() method did it for us.
Knowing that it's going to have to check the id attribute on
every incoming element to find a match for the element it's just sent
off for us, the ask() method makes sure that the outgoing
element actually has an id attribute. If it doesn't, it
adds one, giving it a unique value. That way, it stands a fighting
chance of returning to the caller something this side of the end of
time.

{{Note|Talking of time, if you're uneasy about calling blocking
functions in general, you can always set an alarm() to
interrupt the call after a certain length of time.

</code>
For more information on matching requests and responses, see Section 2.5
in Chapter 2.

Now let's move on to the response to IQ-get, which we now have in
$result. While we're expecting an IQ-result in response to
IQ-get, the request might not have been succesful, and we simply bail
out gracefully if it isn't, ending our connection with the Jabber
server:

if ($result->attr('type') eq IQ_ERROR) {
$r->print("Sorry, no connection to the JUD available at ".JUD);
$r->print("</body></html>"); $c->disconnect; return;

}

Otherwise, we're expecting a result, containing the search fields and
some instructions. These will be contained within the query tag
qualified by the jabber:iq:search namespace (the tag is usually
called query, but here, as elsewhere in the script, we're not
taking any chances and are looking for "the first (hopefully the only!)
occurrence of a child tag qualified by the jabber:iq:search
namespace."

my $info = $result->getTag(, NS_SEARCH);
An HTML form is built from the instructions and the search fields; the
instructions are retrieved with:

$info->getTag('instructions')->data
which retrieves the <instructions/> tag and extracts its
contents.

The getChildren() method is called upon our
<query/> tag to discover what fields are available. For
all those fields, barring the "instructions" one, we create an input
text field:

Once the form has been built, the work for this stage is complete—the
form is relayed to the user, who will submit a completed form, thereby
invoking the next state.

State 2: Query the JUD

The submission of the HTML form will
cause a number of name/value pairs to be passed as part of the HTTP GET
request. These names and values are captured into the @a array
as described earlier. If we have more than one entry in @a, we
know it's a form submission and must respond to that by querying the JUD
and returning the results. In this case, as we know that the contents of
@a are name/value pairs, we can view those contents as a hash,
using a new variable %a:

elsif (scalar @a > 1) { my %a = @a;
Now, %a will contain entries where the keys are the names of
the search fields and the values are the values entered in the form.

In the same way that we constructed an IQ-get to query the JUD for the
search fields and instructions, we construct an IQ-set to perform the
actual query:

Using the information in %a, we insert tags for each of the
search fields for which a value was specified in the form. For example,
if only the value adams was specified, in the field
representing the <last/> search field, as shown in Figure
10-3, we would only want to insert:

Also in a similar way to handling state 1, we make the call by using the
ask() method; the response will be received into the
$result variable as an object representation of the IQ-result
(or IQ-error) element:

otherwise proceeding to extract the <query/> tag from the
search result. This tag will contain the <item/>s
representing the JUD entries found to match the search criteria
submitted (see the response in Example 10-2):

my $info = $c->ask($iq)->getTag(, NS_SEARCH);
We want to display a simple table of results, with each table row
representing an <item/>:

In this section of the code, we also make the link between the first and
second level of the address book. The JID, specified in each
<item/> tag's jid attribute, is the key to the
JUD entry that the item represents and also the key to the vCard of the
user that has that JID. For each of the item lines in the table, we need
to build a selectable link to lead the user to the second level, which
allows him to view the vCard. This is what we want each link to look
like:

unless (length($tag->data) == 0 or $flag++)
serves to ensure that the link is made on a single, nonempty field in
the JUD item. When registering with a standard JUD, none of the fields
are compulsory, so it's quite possible for there to be missing values
returned in the search results. So we want to make sure that the
<a href="...">...</a> link that we build actually
surrounds some value; otherwise, it wouldn't be clickable. The
$flag variable just ensures we build only one link and not a
link for every nonempty field.

Finally, the number of items found is displayed with:

$items++;

}
$r->print("</table>\n");

$r->print("<p>$items results found</p>");

}

State 3: Retrieve a vCard

If we receive a single argument in
the request, we'll take it to be a JID, passed from the link in the
table build in the previous state, and immediately build an IQ-get to
retrieve the vCard. The JID is to be found in the first element in the
@a array—$a[0].

Notice how the name of the query tag in this query is not
query, but vcard. See Section 6.5.1 in Chapter 6 for
details.

The retrieval query is in the form of an IQ-get, rather than an IQ-set.
As we needed to send information in our JUD query (the search criteria),
an IQ-set was appropriate. Here, an IQ-get is appropriate as we're not
including any information to qualify our request; all we need to send is
this:

<iq type='get' to='dj@gnu.mine.nu'> <vcard
xmlns='vcard-temp'/> </iq>
After sending the <iq/> element, waiting for the
response, and checking for any errors, we extract the vCard detail and
display it:

The structure of the vCard namespace is rather complicated and
long-winded, and it's common for many of the fields to remain unfilled.
So to keep the script simple, we're going to display all the top-level
fields that aren't empty:

General handler close

Once we've dealt with the possible
states, we send the closing HTML statements common to all three of them,
and disconnect from the Jabber server:

$r->print("</body></html>"); $c->disconnect;

return;

At this stage, there's nothing more for the module to do. Having
discerned the state from the arguments in the URL (and thereby the
appropriate action) and having carried out that action, the module ends,
handing control back to its mod_perl host. Remembering that
Demo::JBook is an Apache handler in the form of a Perl module, we
need to ensure that the module itself returns a true value (as with any
Perl module):

}

1;

Notes for Improvement

The Demo::JBook script is merely an
example. On top of tightening up the error and exception handling, there
are a few other things that you might want to consider doing to improve
upon it:

Jabber connectivity

As mentioned already, you'll probably want to improve the connection

efficiency to the Jabber server by holding a socket open and sharing

this connection across multiple calls to the handler.

Choice of JUD

The JUD to be queried is fixed; you may prefer to allow the user to

select which JUD will be searched. What's more, selection of more than

one JUD would allow a powerful search across public Jabber user

directories.

Key handling

We've seen how a JUD is queried in Example 10-2. Some JUDs use the

simple key-based security and pass an additional <key/>

tag containing random data—a sort of session key, as described in

Section 6.2.11 and Section 6.2.13. Any <key/> tag

received from the JUD in response to an IQ-get must be sent back

verbatim to the JUD in the subsequent IQ-set. Otherwise the search

will fail, and you'll get a response similar to that shown in Example

10-7.

Visual impact

Last but not least, the visual impact of the end result as shown here

(in Figure 10-3, Figure 10-4, and Figure 10-6) lacks a certain

something. You might want to do something about that—give it a grander

design, make it more pleasing, or at least interesting, to the eye.

The HTML has been kept deliberately basic in this recipe, so as not to

XML-RPC over Jabber

XML-RPC is an easy way to get software that's
running on different operating systems to be able to make and respond to
procedure calls (the "RPC" part of the name stands for "Remote Procedure
Call") over the Internet.

The basis of XML-RPC is straightforward and is described at XML-RPC's
home page (http://www.xml-rpc.com). The procedure calls, each consisting
of the name of the procedure (or method) to call and a set of
arguments to go with that call and the corresponding responses, each
consisting of a set of results, are encoded in an XML-based format. The
requests and responses, so encoded, are exchanged over HTTP, carried as
the payloads of POST requests.

Example 10-8 shows a typical request in XML-RPC encoding. It's calling a
procedure called examples.getStateName, and passing a single
integer parameter with the value 41.

<?xml version="1.0"?> <methodResponse> <params>
<param> <value><string>South
Dakota</string></value> </param> </params>
</methodResponse>
The choice of the word "payload" to describe the encoded requests and
responses is significant: each request, headed with an XML declaration
(<?xml version="1.0"?>) and encapsulated as a single tag
(<methodCall/>), and each response, also headed with an
XML declaration and encapsulated as a single tag
(<methodResponse/>), are succinct, fully formed, and
complete parcels that have meaning independent of their HTTP carrier.

While the XML-RPC specification stipulates that these parcels be carried
over HTTP, we could take advantage of the power and simplicity of the
encoding and carry procedure calls and responses over Jabber.

Jabber-RPC

Jabber-RPC is the name given to the marriage of
encoding from the XML-RPC specification and Jabber as the transport
mechanism. As well as building upon a stable specification, Jabber-RPC
brings advantages of its own to the world of procedure calls over the
Internet. Many of the potential XML-RPC responders are HTTP servers
behind corporate firewalls or one-way Network Address Translation (NAT)
mechanisms and are therefore unreachable from the Internet. Substituting
Jabber as a transport gives calls a better chance of reaching their
destination. If a Jabber-RPC responder—a program that connects to a
Jabber server, is addressable by a JID, and can receive (and respond to)
XML-RPC-encoded calls—is connected to a Jabber server visible to the
Internet, then request calls have to make it only to that Jabber server,
and internal packet routing within the server will allow the parcels to
reach their destinations behind the firewall, whether those destinations
are client-based or component-based responders.

It should be clear by now that the idea of Jabber-RPC is to transport
the XML-RPC-encoded parcels in an extension, an attachment, to an IQ
element. Just as the details for a search attempt (for example of a
Jabber User Directory) are carried in an IQ-set extension qualified by
the jabber:iq:search namespace (as shown in Example 10-2), so
the Jabber-RPC method calls are carried in an IQ-set extension qualified
by a namespace of its own. This namespace is a new one and is
jabber:iq:rpc.[1]

Similarly, as the results of a JUD search are returned in an IQ-result,
so Jabber-RPC method responses are returned in an IQ-result. Example
10-10 shows a simple Jabber-RPC-based method call and response, using
the same XML-RPC-encoded parcels as shown in Example 10-8 and Example
10-9.

The only major difference between the payloads as carried in an HTTP
POST and the payloads as carried in an <iq/> element is
that there's no XML declaration. It's not possible of course, when you
consider the context of the IQ elements. They're document fragments
in the XML stream between the requester (or responder) and the Jabber
server. As explained in Section 5.3, these documents are fanfared with
their own XML declaration, and any further declaration within the
document is illegal from an XML point of view.

{{Sidebar|Jabber-RPC Requesters and RespondersA quick word about
Jabber-RPC requesters and reponders. Connection to a Jabber server is
possible in two main ways, as we've seen in the recipes so far: as a
client via the JSM or as a component connected directly to the jabberd
backbone. At the simplest level, all a Jabber-RPC requester or responder
is, is something that makes a Jabber connection, sends and receives IQ
elements, and uses a standard XML-RPC library to encode, decode, and
service the requests and responses.

It doesn't matter whether the requesters and responders are built as
clients or components. One could argue that the Jabber client model fits
more naturally into the role of a requester, and the Jabber component
model fits more naturally into the role of a responder, but this isn't a
requirement. Indeed, if you're not the Jabber server administrator and
don't have access to the server configuration (to be able to insert a
<service/> stanza for a new component—see Section
9.3.1.1), building a Jabber-RPC responder as a Jabber client may be the
path of least resistance.

</code>

Building a Requester and a Responder

Let's have a look at the
power and flexibility of Jabber-RPC and how to build requesters and
responders. We're going to build a client-based requester, in Python,
and a client-based responder, in Java. There are two great XML-RPC
library implementations for Python and Java that we'll use to do the
legwork for us. Figure 10-8 shows what we want to build.

To keep things fairly simple, we'll just implement and call something
similar to the getStateName() function already shown in the
examples: getCountyName().

The responder: JabberRPCResponder

We'll start up the Java
client-based Jabber-RPC responder (imaginatively called
JabberRPCResponder) specifying a handler class that is to service
the XML-RPC-encoded method calls, get it to connect to a Jabber server,
and listen for incoming Jabber-RPC requests. It will use the Helma
XML-RPC library (at http://xmlrpc.helma.org) to service incoming
requests using the handler class.

Being a Java script, we'll use the JabberBeans library. We'll need to
extend the library's capabilities to handle extensions in the new
jabber:iq:rpc namespace.

The requester: JabberRPCRequester

The Python client-based
Jabber-RPC requester will also start up and connect to a Jabber server.
We'll use a different server than the one the responder is connected to;
after all, the whole point of XML-RPC and Jabber-RPC is to make the RPC
world a smaller and bigger place at the same time, through the power of
the Internet. We're going to use the Pythonware XML-RPC library
(http://www.pythonware.com/products/xmlrpc/index.htm) to encode a
getCountyName() request and decode the response.

JabberRPCResponder

Let's look at the Jabber-RPC responder first.
There is a single script, called JabberRPCResponder, but there are
also a number of supporting classes that we need. Let's take things one
at a time.

The RPCHandler class

The Helma XML-RPC library implementation
allows you to build XML-RPC responders independent of any particular
transport by using an instance of the XmlRpcServer object,
which represents an abstraction of an XML-RPC server. You can then
construct a class—the handler class—containing your methods to be
callable via XML-RPC. The calling of these methods is coordinated by the
XmlRpcServer object, which you tell about your handler class
using the addHandler() method.

The IQRPC classes

JabberBeans deals with Jabber
element extensions—<query/> and <x/>
tags—using individual helper classes. We've seen this in the
HostAlive recipe in Section 8.2, where the
IQAuthBuilder class is used to construct an authorization
extension:

<query xmlns='jabber:iq:auth'>
<username>alive</username> ... </query>
The helper classes are oriented around namespaces. Because we have a new
namespace to deal with (jabber:iq:rpc), JabberBeans
needs to have helper classes to handle extensions in that namespace.

We need a minimum of two helper classes. We need a class that represents
an extension—a <query/> tag—in the jabber:iq:rpc
namespace. If we are going to construct IQ elements containing such
Jabber-RPC extensions, we also need a class to build those
extensions. The class that represents the jabber:iq:rpc
extension is called IQRPC, and the class that is the builder
for the extensions is called IQRPCBuilder.

For an example we're already familiar with, look at the steps leading up
to the authorization phase in the HostAlive script (and indeed our
JabberRPCResponder script, which is shown in the next section); the
authorization IQ element is constructed as follows:

namespace is built with an authorization builder, iqAuthb,
which is an instance of IQAuthBuilder.

Values for the tags within <query/>, such as <username/> and

<password/>, are set using methods belonging to that builder class.

The actual extension is generated with a call to the

build() method. Using addExtension(), the newly
generated extension is added to the container representing the
<iq/> element being constructed by the IQ
builderiqb, an instance of InfoQueryBuilder.

Figure 8-3 shows the relationship between these builders and the things
they create. (For a review of what's required to authenticate with a
Jabber server, see Section 7.3.)

Although necessary, the IQRPC classes aren't the focus of this recipe
and can be found in Appendix B. They are essentially modified copies of
the classes for the jabber:iq:time namespace: IQTime
and IQTimeBuilder. They just need to be compiled and made
available in the $CLASSPATH. Putting them in the same directory
as the JabberRPCResponder script will work fine.

The JabberRPCResponder script

Example 10-11 shows the
JabberRPCResponder script in its entirety. In the next section we'll
take it piece by piece.

Looking at JabberRPCResponder Step by Step

Now let's examine the
JabberRPCResponder script step by step so we can see how it works:

import org.jabber.jabberbeans.*; import
org.jabber.jabberbeans.Extension.*; import
org.jabber.jabberbeans.util.JID; import java.net.InetAddress; import
java.util.Enumeration; import java.io.*; import helma.xmlrpc.*;
We need to bring in the jabberbeans classes as shown, as well
as some core Java features that we'll see used in the script a bit
later: an InetAddress to represent the Jabber server's
hostname, an Enumeration interface to access the extensions in
the incoming IQ elements, and java.io features for feeding the
XML-RPC-encoded requests to the XmlRpcServer object. We also
bring in the classes from the Helma XML-RPC library.

The definition of our JabberRPCResponder class looks similar to
that of the HostAlive class in Section 8.2. However, rather
than merely connecting to a Jabber server and sending packets off down
the stream, we want to listen for incoming packets—in this case, IQ
elements carrying jabber:iq:rpc-qualified payloads—and handle
them. Accordingly, we specify that our main class implements
PacketListener, a JabberBeans interface that Jabber clients use
to receive notification of incoming packets. The interface describes
three methods: receivedPacket(), sentPacket(), and
sendFailed(). We'll define our receivedPacket()
method, described later in this section, to catch and process the
incoming Jabber-RPC requests.

We're going to use an XmlRpcServer object, in
responder, to provide the translation services between our
RPCHandler class that contains the methods we want to "expose,"
and the XML-RPC-encoded requests and responses.

Naturally, we also need a JabberBeans ConnectionBean, which
we'll hold in cb.

public JabberRPCResponder() { responder = new XmlRpcServer();
responder.addHandler("examples", new RPCHandler());

}

The JabberRPCResponder class constructor,
JabberRPCResponder(), will be called in the main()
method later in the script. It is used to create an instance of the
Helma XmlRpcServer object, and associate our
RPCHandler class with it, as a handler for method calls.
The addHandler() method takes two arguments: the first is the
method prefix name, such as examples in the methodName
specification:

<methodName>examples.getCountyName</methodName>
and the second is the object—an instantiation of our handler class—which
contains the methods that the XmlRpcServer will use to
"service" requests. In other words, the XmlRpcServer object
will determine that a call to examples.getCountyName should be
handled, as getCountyName, by the RPCHandler object.

We instantiate our JabberRPCResponder object into
server and call the start() method. Various functions
in start() can raise exceptions; we take care of them all here
with a simple catch clause and abort if necessary, rather than
include all of start()'s functions here and have multiple
try and catch statements:

Most of the content of this start() function should be fairly
familiar. We instantiate a ConnectionBean and will use that, in
cb, throughout the script to send elements to the Jabber
server. Our PacketListener is added as a listener to the
ConnectionBean, causing a method of that interface
(packetReceived()) to be invoked when an incoming element
appears on the stream managed by that ConnectionBean.

In the same way as described in Section 8.2.2 in Chapter 8, we build our
IQ element to authenticate, and send it to the server with the
ConnectionBean's send() method. We also send an
initial availability, in the form of a simple <presence/>
element to the server.

Having connected to and authenticated with the server and sent initial
availability, we can sit back and relax. All the subsequent activity
will be initiated by the arrival of IQ elements on the stream. As
described already, these will be made known (and available) in the form
of calls to the receivedPacket() method of the
PacketListener interface:

The method receives a PacketEvent object, which represents both
the event of a packet (an element) arriving and the packet itself,
which we retrieve into packet with the getPacket()
method. For debugging purposes, we print out what we get.

Now to determine what has actually arrived:

if (packet instanceof InfoQuery) { Enumeration e =
((InfoQuery)packet).Extensions(); if (e.hasMoreElements()) { Extension
ext = (Extension)e.nextElement();
We check to see if the element is an IQ element, and, if so, we retrieve
the extensions—the <query/> tags—that the element
contains. Calling the Extensions() method on the
packet object returns an Enumeration of those tags.

We're expecting only one tag, and we pull that into ext using
the nextElement() method on our Enumeration.

{{Note|For the sake of simplicity, we're not checking here whether the
<query/> tag received is qualified by the
jabber:iq:rpc namespace. You might wish to do that in your
version.

</code>

String request = ext.toString(); String id =
((InfoQuery)packet).getIdentifier(); JID from =
((InfoQuery)packet).getFromAddress();
We can pull the extension into string form, with the toString()
method, ready for passing to our XmlRpcServer object. At this
stage, ext contains the complete XML-RPC-encoded parcel,
starting with the <methodCall> opening tag:

<methodCall>
<methodName>examples.getCountyName</methodName>
<params> <param>
<value><i4>14</i4></value> </param>
</params> </methodCall>
So that we can send a response back to the requester, we need two other
things from the incoming element besides the actual request payload. In
general, when an <iq/> element is sent, representing a
request, either as an IQ-get or an IQ-set, the sender is expecting the
response, either an IQ-result or an IQ-error, with the same value in
the id attribute. This is so the responses, once received, can
be matched up with the original requests. So we need to store the
id value, available to us through the getIdentifier()
method of the packet object. We also need the JID of the
sender, retrieved with the getFromAddress() method, so we can
specify it in the to attribute of the IQ-result we'll be
sending back.

Now it's time to service the request:

ByteArrayInputStream bis = new
ByteArrayInputStream(request.getBytes()); String result = new
String(responder.execute(bis));
With the execute() method of our XmlRpcServer object
in responder, we convert the <methodCall/> into
a <methodResponse/>, in effect. The decoding of the
XML-RPC-encoded request, the determination of which method in which
class to call (in our case it will be the getCountyName()
method in our RPCHandler class), the calling of that method,
and the encoding of the result into an XML-RPC-encoded response are all
done magically and transparently for us by XmlRpcServer (thank
goodness, or this recipe would be unbearably long!).

There's a bit of jiggling about required before we can make the
execute() call, though. The method is expecting an
InputStream object, and we've got a String. Not to
worry, we just convert it with a ByteArrayInputStream, which
can take a byte array (from request.getBytes()) and produce an
InputStream object bis. Similarly, execute()
produces a byte array, so that is converted to a String by
wrapping the call with String().

The incoming XML-RPC-encoded request, being carried in the IQ element,
will not include an XML declaration. Luckily, the XmlRpcServer
does not mind that one is not present before decoding and dispatching
the request. It will, however, include one on the encoded response it
emits. So we must check for that and strip it off if there is one:

Now we have everything we need to return the XML-RPC-encoded response to
the requester. It's time to call on the services of our IQRPC
classes:

IQRPCBuilder iqrpcb = new IQRPCBuilder();
iqrpcb.setPayload(response);
We create an instance of the IQRPCBuilder and call the single
method setPayload() to load the XML-RPC-encoded response into
the <query/> tag, which is qualified by the
jabber:iq:rpc namespace. This is effectively the "extension" in
JabberBeans parlance:

After creating an IQ element container using an
InfoQueryBuilder and setting the appropriate attributes, we
generate the <query/> tag (iqrpcb.build()) and
add it to the IQ element container with addExtension().

The <iq/> is then generated—it now contains our
payload—and sent back to the server, where it will be routed to the
original requester.

That's pretty much all there is to it. We have a couple of other methods
belonging to the PacketListener interface:

}
We fill these methods with debugging-style output statements for our
convenience.

JabberRPCRequester

Now that we've got our Jabber-RPC responder
all set up, it's time to turn our attention to our requester,
JabberRPCRequester, shown in Example 10-12. This is a Python script
that uses the Jabberpy library and Pythonware's
xmlrpclib library. It's a pretty simple affair.

Endpoint = 'server@gnu.mine.nu/jrpc-server'; Method =
'examples.getCountyName';
The script connects to the Jabber server defined in Server,
with the username defined in Username. The resource that will
be passed in the authentication request is jrpc-client. There
is as much significance in this name as there is in the name of the
resource used by JabberRPCResponder (jrpc-server): none.
It's just a useful naming convention to adopt when writing requesters
and responders.

A single parameter, which will be interpreted as the index of the county
to retrieve via the call to examples.getCountyName, is
expected.

county = int(sys.argv[1])
The method expects an integer, so we convert it directly. This has a
favorable secondary effect when we come to XML-RPC encode the request;
if we hadn't called the int() function and left
county as a string, this is what the XML-RPC-encoded parcel
would have looked like:

<methodCall>
<methodName>examples.getCountyName</methodName>
<params> <param>
<value><string>1</string></value> </param>
</params> </methodCall>
However, this is what we really want:

<methodCall>
<methodName>examples.getCountyName</methodName>
<params> <param>
<value><int>1</int></value> </param>
</params> </methodCall>
In the same way as in the previous Python recipes, we connect to the
Jabber server and authenticate:

con.auth(Username,Password,Resource)
Now all we have to do is compose our XML-RPC-encoded request and send it
on its way to the Jabber-RPC responder, which in our case, identified
here by the JID in the Endpoint variable, is our
JabberRPCResponder script.

It's time to call on the services of the XML-RPC library. We use the
dumps() function to build an XML-RPC encoding, passing the
single parameter (in a tuple, which is required by dumps())
representing the county index and the name of the method to call:

request = xmlrpclib.dumps(((county),), methodname=Method); We
build the IQ-set containing a <query/> element in the
jabber:iq:rpc namespace, by creating an instance of an
Iq object, and calling methods to specify that namespace
(setQuery()) and insert the XML-RPC encoding as the payload
(setQueryPayload()):

iq = jabber.Iq(to=Endpoint, type='set')
iq.setQuery('jabber:iq:rpc') iq.setQueryPayload(request) At this
stage, we need to send it off and wait for a response. You have probably
noticed that, unlike in previous Python recipes, this script hasn't
defined or registered a callback to handle incoming elements. This is
because we're approaching the task in a slightly different way in this
script. The method used to send the element to the Jabber
server—SendAndWaitForResponse():

result = con.SendAndWaitForResponse(iq)
is the rather verbose cousin of the Jabber::Connection
library's ask() method, as described in Section 10.1.5.3. It
does "exactly what it says on the tin," namely, send the element off to
the server and block until an element is received that is deemed, by a
matchup of the id attribute values on both elements, to be the
response. If no id attribute exists on the outgoing element, it
is stamped with one (with a value unique within the current usage of the
jabber class). Both are also relations of
Net::Jabber's SendAndReceiveWithID().

So result receives an element object. It's an IQ element, as
that is what a response to an IQ element will be. If the call
transport (as opposed to the call itself) is successful, the
response will be an IQ-result. If not, for example, if the
Endpoint that we specified doesn't exist, then the response
will be an IQ-error.

if result.getType() == 'result': response =
str(result.getQueryPayload()) parms, func = xmlrpclib.loads(response)
print parms[0] else: print "Could not complete call"
Conversely, while the call transport might have succeeded, the call
itself might have failed, for example, if we had specified a nonexistent
method name in Method. However, for our purposes, we're going
to assume that the call was successful. So we grab the payload (i.e.,
the contents of the <query/> tag) and string it with
str().

The loads() function of xmlrpclib is used to extract
the details from an XML-RPC-encoded parcel; it will produce two values,
which we capture in resp and func. The first is the
set of parameters, common to either a request or a response, and the
second is the method name, if it exists in the encoding. We want to
decode a <methodResponse/> here, so there's no method
name present, but there should be a set of parameters.

We take the first (and only) element from the set of parameters in
resp and print it out.

Jabber-RPC in Perl

Exciting as this recipe might be, it's not
very visual. There are no screenshots of note to show, just a couple of
STDOUTs from two scripts started at the command line. To fix this
"problem," we're going to round this recipe off with a quick look at
Jabber-RPC in Perl. Based on the Jabber::Connection library is
a fairly new Perl library called Jabber::RPC, which sports two
modules: Jabber::RPC::Client.pm and
Jabber::RPC::Server.pm.

If you're slightly perplexed about what it takes to extend Jabber
support in Java, take a look at Example 10-13 and Example 10-14. The
first is an implementation, in Perl, of the JabberRPCResponder and
all its class periphery. The second is an implementation of our
JabberRPCRequester.

That's it. There's no more code than that. So don't lose heart. Just use
Perl; you know it makes sense.

Browsing LDAP

Browsing, a relatively new Jabber feature
(introduced in Section 2.9 and described through its associated
namespace in Section 6.2.5), is an extremely powerful and flexible
beast. Whereas many of the standard Jabber namespaces such as
jabber:iq:oob, jabber:iq:auth, and
jabber:iq:register busy themselves with providing context for
relatively narrow areas of information (out-of-band information
exchange, authentication, and registration, respectively),
jabber:iq:browse is a namespace that qualifies, and therefore
defines, a flexible container that can be used to carry all sorts of
information—information that is wrapped in meaning and context and that
can have virtually unlimited levels of hierarchy. What's more, it can
deliver information in a standard way that can be understood by Jabber
clients.

{{Note|As of this writing, the only Jabber client to implement browsing
is WinJab.

</code>
Put another way, it means that we can extend the scope of Jabber
deployment to areas outside what we traditionally see as the "Jabber
world." The power and simplicity of the Jabber browsing namespace design
means that we can adapt its use to whatever purpose we have in mind. As
we push out the Jabber protocol and give entities within our networks
the gift of speech and presence (in other words, give them a JID and
graft on a Jabber connection stub), we will want to identify those
entities as something more than a collection of hazy objects that sit
behind Jabber addresses.

Want to find out about dictionaries that are reachable by Jabber?

Browse to a directory and pull out those JIDs—the dictionaries'
addresses—that are identified as keyword/dictionary JID-types.

Want to allow your users to navigate an information hierarchy outside

the Jabber space but from within the comfort of their Jabber client?
Build a "reflector" that navigates the hierarchy on behalf of your
Jabber users and transforms it into a Jabber browsing context—by
formulating the information in jabber:iq:browse terms. Jabber
browsing is one of those oddities that is so simple and so ultimately
flexible that it's sometimes better to demonstrate it than to talk about
it. Let's have a look at what browsing can do by building ldapr, a
"reflector" for information in a database accessed by the Lightweight
Directory Access Protocol (LDAP).

Building the Reflector

We're going to build the reflector as a
component that connects directly to the Jabber backbone. This makes
sense, as it's a service that we'll probably want to run continuously
(and perhaps start up and shut down in conjunction with the Jabber
server itself), rather than something more transient like a client-based
'bot, for example.

Before we go any further, have a look at Figure 10-9.

</code>

This figure shows WinJab's browser window, which, when first opened,
requests the top-level browse information from the Jabber server that
WinJab is connected to (cicero, in this case). This is the
information in the <browse/> section of the JSM component
custom configuration, as described in Section 4.4.3.8, which looks like:

<browse> <conference type='public' jid='conf.cicero'
name='Public Chat'/> </browse> We can see just one icon,
representing the Public Chat conference service.

WinJab sensibly uses this location—the Jabber server (specifically the
JSM) itself— as a starting position for browsing navigation. From the
description of jabber:iq:browse in Section 6.2.5, we know that
each element within a <browse/> section is identified
with a JID, in the jid attribute. Here, the Public Chat element
has a JID of conf.cicero. If we click on the element's icon in
WinJab's browser window, it would make a further browse request—an
IQ-get with an empty extension qualified by the
jabber:iq:browse namespace—to that JID. Thus is a browse
hierarchy descended. Example 10-15 shows what that browse request might
look like.

Identifying ldapr in the browsing hierarchy

This is exactly
where our reflector service, in the form of a script called ldapr,
will fit in. In the same way as we described our RSS news agent
(newsagent) in the JSM configuration's <browse/>
section, we'll now describe ldapr. We add it, like this:

<browse> <conference type='public' jid='conf.cicero'
name='Public Chat'/> <service type='x-ldap' jid='ldap.cicero'
name='LDAP Reflector'/> </browse>
There are a couple of things to note in the definition:

service/x-ldap

The LDAP reflector is a service, so we use the service

category for the tag name used to describe it. While there are already

many subtypes defined within the jabber:iq:browse namespace

(such as irc, aim, and jud), none of them

matches what this service is going to offer, so in the same way as we

invent Multipurpose Internet Mail Extensions (MIME) subtypes in the

x- space, we specify x-ldap for the type

attribute here.

ldap.cicero

Each component has a JID. In browsing, the JID is the key to

navigation. When the icon representing this service is clicked, it's

to this JID that the browse request is sent. To provide a smooth

navigational path through the hierarchy, it's up to the component to

return browse data items that are identified by JIDs appropriate for

further navigation. We'll see what this means in the next section.

Navigating into the LDAP hierarchy

Having our reflector
component defined in the JSM's <browse/> list makes for a
smooth transition into the reflection. We're going to navigate the LDAP
hierarchy in a similar way to what was described in Section 6.2.5.1. On
receipt of an initial browse request, ldapr must return the top
level of the LDAP hierarchy it has been set up to reflect:

<iq type='get' to='ldap.cicero' id='7'> <query
xmlns='jabber:iq:browse'/> </iq>
Figure 10-10 shows the LDAP hierarchy we've discussed in Section
6.2.5.1. It's part of an imaginary structure devised to represent people
and departments in an organization. The base distinguished name, or
base DN (an LDAP term meaning the common suffix used in the
identifiers of all elements in a particular LDAP structure), is
dc=demo,dc=org. A DN, or distinguished name, can be thought
of as a key for a particular element. Levels within the LDAP structure
are identified with DNs of ever-increasing lengths, as they get more
specific the deeper the hierarchy is descended.

</code> If

ldapr were to reflect this hierarchy, we want the response to
an initial browse request to return information about the
People and Groups nodes. In Example 10-16, we see what
this response looks like.

An initial browse request elicits a reflection of the People and
Groups nodes

This is in slight contrast to the browse <iq/> elements
shown in Section 6.2.5.1, in which, effectively, two levels of hierarchy
are returned:

RECV: <iq type='result' id='B89' to='dj@cicero/basement'
from='ldap.cicero'> <item name='root entry'
xmlns='jabber:iq:browse' jid='ldap.cicero'> <item name='ou=People'
jid='ou=People@ldap.cicero'/> <item name='ou=Groups'
jid='ou=Groups@ldap.cicero'/> </item> </iq>
It's really up to you to decide how many levels of information you want
each browse response to emit. It depends on circumstances (will the
requester be able to interpret multiple levels of hierarchy?) and the
type of application scenario in which you're wanting to employ Jabber
browsing. In this case, despite the difference in appearance, WinJab
will interpret the information correctly whichever way you play it.

{{Sidebar|No Query Tag?This last example looks slightly odd, because the
familiar <query/> tag, usually the container for
information within various IQ namespaces, is conspicuously absent. It's
actually still there in spirit, in the form of the first
<item/> tag, which takes the <query/>
tag's role in carrying the xmlns='jabber:iq:browse' namespace
declaration.

</code>
So, we've got our first two LDAP levels back. Figure 10-11 shows how
they're displayed in WinJab's browser window. The JID we've just browsed
to—ldap.cicero—is shown in the Jabber Address field. The
People and Groups nodes are represented by
<item/> tags within the
jabber:iq:browse-qualified result; these are translated into
folder icons in the browser window.

</code> The browser displays folder

icons for these nodes because we've described them using
jabber:iq:browse's generic "holder" tag <item/>.
While the namespace describes many categories to represent many
different things (service, conference, user, and so on), there's not
really a category that fits the "LDAP_ hierarchy node" description. So
we plump for the generic <item/>, reserved specially for
such occasions.

To navigate to the next level in the hierarchy, all we do is click on
one of the icons now displayed to us. WinJab will send this next browse
request to the JID that's associated with the icon we click. In the case
of the People icon, we know from the browse result shown in Example
10-16 that the JID is ldap.cicero/ou=People. This is what that
browse request and response look like:

RECV: <iq id='B105' type='result' to='dj@cicero/basement'
from='ldap.cicero/ou=People'> <query xmlns='jabber:iq:browse'>
<item jid='ldap.cicero/ou=UK, ou=People' name='ou=UK'/> <item
jid='ldap.cicero/ou=France, ou=People' name='ou=France'/> <item
jid='ldap.cicero/ou=Germany, ou=People' name='ou=Germany'/>
</query> </iq>
The results of this browse request are shown in Figure 10-12. Again,
notice the JID displayed in the Jabber Address window—it's the JID that
we've just browsed to. The JIDs that are assigned to the items displayed
here reflect the next level in the hierarchy. And so it goes on.

</code>

What the reflector is actually doing

Each of the JIDs that are
browsed to look like this:

[component name]/[relative LDAP DN] such as:

ldap.cicero/ou=UK, ou=People The hostname part of the
JID—[component_name]—is the name of the LDAP
reflector component, and the resource part of the
JID—[relative_LDAP_DN]—reflects the DN of the node
(minus the base DN suffix) within the LDAP structure that the
<item/> with that JID represents.

The crucial bit is that the [component_name] part
remains the same (ldap.cicero) across every call. This means
that all of the jabber:iq:browse requests made in the
navigation sequence go to the component, in this case, the ldapr
script.

Once the component receives these requests, it disassembles the JID,
extracting the resource part, and uses it to query the LDAP server on
the Jabber client's behalf. It then builds a response, in the form of an
IQ-result containing the all-important
jabber:iq:browse-qualified extension, containing the results of
the LDAP query.

JIDs are used to convey information to the component, as well as to
have the request delivered to the right place.

{{Sidebar|Using JID Parts to Convey InformationThe example from Section
6.2.5.1 showed the information being conveyed as the user part of
the JID:

<item name='ou=People' jid='ou=People@ldap.yak'/>
This is perfectly fine, as long as the information we want to convey
doesn't contain characters deigned illegal for that part of the JID.
There are far fewer restrictions on how the resource part of a JID may
be constructed. So it should usually be your first choice of location
for piggybacking information:

<item jid='ldap.cicero/ou=People' name='ou=People'/>
</code>

The ldapr script

While the explanation might be long, the actual
script, shown in Example 10-17, is relatively short. Written in Perl,
the ldapr script uses the Jabber::Connection library.

The ldapr script, written in Perl

use strict; use Jabber::Connection; use Jabber::NodeFactory; use
Jabber::NS qw(:all); use Net::LDAP;

Looking at ldapr Step by Step

Taking the script step by step, we
start on familiar ground:

use strict; use Jabber::Connection; use Jabber::NodeFactory; use
Jabber::NS qw(:all); use Net::LDAP;

my $ldapsrv = 'cicero'; my $basedn = 'dc=demo,dc=org';
After declaring the modules we want to use (the only one we haven't seen
so far is the Net::LDAP module that we'll need to connect to
and query an LDAP server), we define a couple of variables.
$ldapsrv is the name of the LDAP server that ldapr is going
to be reflecting, and $basedn is the base distinguished name
that will be used as the suffix in all of the LDAP queries.

If you don't have an LDAP server of your own, a number of public ones
are available that you could point this script at. The two variables
$ldapsrv and $basedn go together—make sure you specify
the correct base DN for the LDAP server you want to reflect.

{{Note|Depending on the configuration, some LDAP servers will require
you to bind to them with a username and password before you can
perform searches. To do this, you'll need to include an extra step in
this script, using the bind() method in Net::LDAP.

</code>
Having opened our connection to the LDAP server:

my $ldap = Net::LDAP->new($ldapsrv) or die $@;
we then proceed to connect to the Jabber server as a component. We're
connecting to localhost:9389, which means this component script
is going to run on the same host as the Jabber server, and connect to it
on port 9389:

unless ($c->connect()) { die "cannot connect: ".$c->lastError; }
We'll also need a <service/> stanza in the Jabber
server's configuration file to describe this component. The stanza in
Example 10-18 would be appropriate. Refer to Section 9.3.1.1 for more
details on connecting to Jabber as a component.

A component instance definition for ldapr

<service id='ldap.cicero'> <accept>
<ip>localhost</ip> <port>9389</port>
<secret>secret</secret> </accept> </service>
The script isn't going to do much apart from reflect
jabber:iq:browse queries as LDAP searches. Consequently, it
doesn't need to be able to handle anything apart from incoming IQ
elements:

debug("registering IQ handlers");
$c->register_handler('iq',\&iq_browse);
$c->register_handler('iq',\&iq_notimpl);
The iq_browse() function is where all the work will be done. As
in the newsagent recipe (Section 8.3)," we also have a
"catchall" function (iq_notimpl()) that cleans up any "stray"
IQ elements that it doesn't know about. Furthermore, because we aren't
registering any handlers for <message/> or
<presence/> elements, the dispatcher in
Jabber::Connection will just throw them away, leaving ldapr
blissfully ignorant of them—which is what we want.

After authenticating with the server by calling the auth()
method to send the <handshake/> element:

debug("authenticating"); $c->auth('secret');
it's time to set the main event loop going. With a call to
start(), we hand over control to Jabber::Connection,
safe in the knowledge that we have a function (iq_browse()) to
deal with the incoming requests that we're supposed to deal with, and
that we don't care a hoot about anything else:

debug("waiting for requests"); $c->start;

Performing the actual reflection

Now, let's move on to the
meat of the script. The main handler, iq_browse(), starts by
making sure it has an IQ element:

What we're looking for is an IQ-get with a
jabber:iq:browse-qualified query extension. If there isn't one,
we exit out of the function, and dispatching continues to the
iq_notimpl() function, because we didn't return the special
value represented by m_HANDLED.

If we do get a valid request, we first extract the relative DN from the
resource part of the JID specified in the IQ-get's to
attribute—the JID. If the request was sent to ldap.cicero/ou=UK,
ou=People, then we extract the relative DN ou=UK,
ou=People into $obj like this:

my ($obj) = $node->attr('to') =~ /\/(.*)$/; debug("request:
$obj");
Armed with a specification of what part of the LDAP hierarchy needs to
be searched, the next step is to call the search() method on
the LDAP object in $ldap:

Calling the all_entries() method on the search results returns
a list of LDAP entries, in the form of objects. Calling the
dn() method on one of these objects (in $entry)
returns us its full DN. Searching with a base of:

ou=UK, ou=People, dc=demo, dc=org
in the demonstration database would return two entries:

cn=Janet Abrams, ou=UK, ou=People, dc=demo, dc=org cn=Paul
Anthill, ou=UK, ou=People, dc=demo, dc=org
The task of this section of the iq_browse() function is to turn
the information, in the form of these two entries, into something like
this:

cn=Janet Abrams, ou=UK, ou=People
For each of the entries found, we insert a tag into the
<query/> extension. The name of the tag is either
item, if the entry is simply an LDAP hierarchy node, or
user (a valid jabber:iq:browse category), if the entry
points to a person. We make the decision on the basis of the first part
of the DN—if it's cn=, then it's a reference to a person. The
isUser() function makes this decision for us.

Once inserted, we embellish the <item/> or
<user/> tag with name and jid
attributes. The jid attribute is crucial, as it represents the
path to further descend within the LDAP hierarchy on the next request.
It is given the whole of the relative DN as a value. The name
attribute is simply given the most significant portion of the DN as a
value.

Whether we found something or not, we still want to return a result to
the requester:

$node = toFrom($node); $node->attr('type', IQ_RESULT);

$c->send($node);

So we swap around the to and from attributes
(remembering we're a component, and not a client) change the IQ
element's type from get to result, and send it back.

We end the function by telling the dispatcher that the element has been
handled:

return r_HANDLED;

}

Supporting functions

The rest of the script consists of minor
functions that play roles in assisting the core iq_browse().

The iq_notimpl() function is exactly the same as in the
newsagent script in Section 8.3; it serves to catch stray IQ elements
that in this case aren't IQ-gets containing a jabber:iq:browse
query, and send them back with a "Not Implemented" error. With IQs, this
is preferable to not responding at all, as responses are usually
expected, even if those responses are IQ-errors. It's different with
<message/> and <presence/> elements, as
they can be seen as "one-way" and valid fodder for an element-sink.

}
As well as sharing the iq_notimpl() function with the
newsagent script, ldapr also shares the toFrom()
function, which flips around the values of the to and
from attributes of an element:

sub toFrom { my $node = shift; my $to = $node->attr('to');
$node->attr('to', $node->attr('from')); $node->attr('from',
$to); return $node;
}
The isUser() function, also described earlier, makes an
arbitrary distinction between LDAP nodes that it thinks are
People and those that it thinks aren't:

sub isUser {

my $rdn = shift; return $rdn =~ /^cn/ ? 1 : 0

}
Last but not least, we have a simple debug() function, which in
this case is simply an abstraction of the classic "print to STDERR"
method:

sub debug {

print STDERR "debug: ", @_, "\n";

}

Building an ERP Connection

To some extent, SAP's R/3, an
Enterprise Resource Planning (ERP) system, has until recently been a
monolithic piece of software, both from an actual and psychological
perspective.

The drive to replace disparate and incompatible business system
"islands" with an integrated software solution that covered business
processes across the board was strong in the 1980s and 1990s. With good
reason. For example, SAP delivered the ability for companies to manage
their different end-to-end processes coherently, without requiring
custom-built interfaces between logistics and financial accounting
packages. The flow of information was automatic between the application
subsystems that were standard within SAP's R/2 and R/3 products.

The upside to this was, of course, the seamless integration of data and
functions within the SAP universe, a universe vast enough to offer
application coverage for pretty much every conceivable business
function.

The downside was, ironically, the universe itself. All-encompassing as
SAP's products were, and indeed they were forever changing and expanding
to meet business demands, it was never enough, if you wanted to break
out of the mould formed by the omnipresent standard proprietary client
called SAPGUI. Sure, these days, with a huge installed base of R/2 and
R/3 systems in production around the world, there are a multitude of
ways to get data in and out of SAP systems, but despite the varied
successes of alternative client initiatives, user interaction with the
business processes remains largely orientated around the SAPGUI.

This recipe is extremely simple compared to the others in this chapter.
It breaks out of the SAPGUI mold and the monolithic software culture to
use open source tools and technologies to add value to our SAP business
processes. The point of this recipe is not particularly what the script
looks like or how it's written, but what it does and how it does it.

Building an Order Approval Notification Mechanism

We're going to
use a standard Jabber client as an SAP R/3 client—obviously not to
replace SAPGUI, rather to allow someone who perhaps has a single
R/3-related task to perform and connects only to SAP occasionally. In
stark contrast to the SAPGUI client, an off-the-shelf Jabber client is
much smaller. It takes up less screen space, memory, and CPU and
generally for focused access is a great way for someone to play his part
in business processes from the comfort of familiar communication
surroundings—his IM client.

Of course, the available Jabber clients don't have any built-in R/3
functionality per se, but as clients that can receive messages and
recognize URLs,[3] they provide enough horsepower for us to achieve our goal.

That goal is to notify a supervisor whenever a sales order is placed
that requires his approval. The notification will arrive in the form of
a <message/> element, carrying some descriptive text and,
crucially, a URL, which points to an Apache-based handler. When invoked,
the handler pulls the relevant information for the order out of R/3,
requests verification of the viewer's identity, and offers a chance to
approve the order with the click of a button.

Effectively we're building a miniworkflow scenario: in one direction a
notification is transmitted out of the bounds of the SAP universe to the
approver, and in the other direction the notification process is turned
around in a one-step approval cycle via Apache. Figure 10-14 shows this
scenario and where our Jabber client and script, called approv, fits
in.

</code> The goal here is to get the

notification message out of R/3 and send it to the supervisor's JID
along with a URL that he can follow back into the R/3 system to carry
out the approval process. The return process via Apache is outside this
recipe's scope and has been left as an exercise for you.

Getting the notification out of R/3

As we've already
mentioned, there are many ways of getting data in and out of SAP. We're
going to use a generic, lowest common denominator feature of the R/3
Basis system to invoke a script and pass it parameters.

{{Note|At this stage, if you're squeamish about R/3 Basis or SAP's ABAP
language or are of another ERP persuasion, it's time to look away. What
we're going to do here is not rocket science, nor is the general process
specific to R/3. We're just going to call a script, at the operating
system level, from within an application inside R/3.

</code>
The function group SXPT encompasses a number of function
modules related to the definition, management, and execution of
operating system commands. Each of these commands is described within
sets of configuration parameters that define how and where they can be
invoked. Using the program RSLOGCOM, you can create definitions for
these operating system commands manually.

We need to define such a command that refers to the approv script.
Figure 10-15 shows the RSLOGCOM definition of approv as an
external command that is called ZNOTIFY.

</code> Once approv has been defined

this way, it can be invoked by passing parameters with a call to a
function module in the SXPT function group
(SXPG_EXECUTE_COMMAND). Example 10-19 shows how the script
might be invoked using this function module in ABAP. Code like this
could typically be installed in a customer exit—a place in a
standard R/3 application where custom processing can be added without
having to go through the involved process of creating and carrying out a
modification request.

if sy-subrc <> 0. raise notification_failed. endif.
We pass two parameters to approv via the
SXPG_COMMAND_EXECUTE: the number of the order that needs
approving (from ordernumber) and the JID of the approver (from
approver). The SXPG mechanism invokes the approv script
with the two parameters, and the notification starts its journey.

The approv Script

As mentioned already, the approv script
(shown in Example 10-20) is rather small and insignificant. Its purpose
is to take the order number it receives, wrap it in a descriptive
message that includes a URL, and send it to the approver's JID. Written
in Perl, the approv script uses a minimum set of features from the
Jabber::Connection library.

$c->disconnect; The script receives the two parameters passed from
R/3 into the $order and $approver variables. Having
connected to the Jabber server at qmacro.dyndns.org and
authenticated as the user approv, it sends off a formatted
message to the approver, before disconnecting.

The script itself is extremely simple and is not of primary importance.
What is crucial here is the role that Jabber is playing. The
<message/> element of Jabber's protocol is used to span
the R/3 world with the IM world, enabling a business process cycle to
take place outside the normal boundaries of R/3 interaction. On a
simple level, that's all it takes to merge the ERP business process
world with the world of IM.

The return path to R/3, via the Apache-based CGI application, is
initiated when the user clicks on the URL in the message, as shown in
Figure 10-16.

</code> Here we see that Jarl has

recognized the http:// address and has rendered it as an active
link. That's all it has to do. There's no attempt on Jarl's part to make
an HTTP request to the Apache server and render the HTML received.
Focusing on "the right tools for the right job," we should leave that
role to a web browser, as indeed Jarl does, starting up the browser of
our choice.[4]

Taking This Further

The journey through the recipes in the last
three chapters of the book has taken us from the simplest CVS
notification mechanism with the cvsmsg script (Section 8.1) through
a fairly involved RSS component (Section 9.3) to the transporting of
XML-RPC-encoded requests and responses with our JabberRPCRequester
and JabberRPCResponder scripts (Section 10.3).

This chapter, and indeed the book, ends on a simple note, in the form of
the approv script. Not without reason, we've completed the circle of
script and application complexity. While we can build very useful and
successful applications that are naturally complex, a Jabber-powered
solution doesn't necessarily have to be. To employ the Jabber
philosophy, the technology, and the protocol elements to build bridges
between previously separate systems, and to span different areas of
technology, open and proprietary alike, using Jabber's open, extensible,
and flexible protocol, is what it's all about.

Finally, it's hopefully clear from the diversity of recipes shown in
this part of the book that deploying solutions with Jabber, as noted in
the preface, really is fun!