1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
|
<?xml version="1.0" ?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<book>
<title>Swiften Developer's Guide</title>
<chapter>
<title>Introduction</title>
<section>
<title>Prerequisites</title>
<para>
We assume that the reader is familiar with the basics of the
XMPP protocol. For an overview of the XMPP protocol and its
workings, see
<citetitle>XMPP: The Definitive Guide</citetitle>
<citation><biblioref linkend="XMPP-TDG"/></citation>
</para>
</section>
<section>
<title>Boost</title>
<para>
Swiften makes heavy use of <emphasis>Boost</emphasis> (<ulink url="http://boost.org"><uri>http://boost.org</uri></ulink>) libraries, including <emphasis>Signal</emphasis>,
<emphasis>Bind</emphasis>, <emphasis>Optional</emphasis>, and
<emphasis>Smart Pointers</emphasis>. We
introduce the basic usage of these libraries in our API throughout
this manual. For detailed documentation, we refer to the <ulink url="http://boost.org">Boost</ulink> website.
</para>
</section>
</chapter>
<chapter>
<title>Tutorial: Writing an Echo Bot</title>
<para>
In this chapter, we guide you through the Swiften API by building an
example XMPP application: an EchoBot. This example program, taken from
<citetitle>XMPP: The Definitive Guide</citetitle>
<citation><biblioref linkend="XMPP-TDG"/></citation>, connects to
an XMPP server, logs in, and responds to all incoming messages with
the exact same message. We build up our application using Swiften's
basic building blocks for XMPP development, to help get a good
understanding of how Swiften fundamental classes work and can be
extended. In the last stage of this example, we
introduce some of Swiften's convenience classes for standard
XMPP tasks such as roster management.
</para>
<sect1>
<title>Connecting to a server: Clients & event loops</title>
<para>
As a first step, we create an application that connects to a server.
The code can be seen in <xref linkend="Example-EchoBot1"/>.
</para>
<example id="Example-EchoBot1">
<title>Connecting to a server</title>
<include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoBot1.cpp.xml" xpointer="xpointer(//programlisting|//calloutlist)"/>
</example>
<para>
The first thing this program does is construct an
<emphasis>Event Loop</emphasis>. An event loop is a seemingly infinite
loop that waits for external events (e.g. incoming network packets,
timers being activated, input happening) to happen; when such an event
comes in, it notifies interested parties of this event, and then
continues listening for the next event. Since many application
frameworks (such as Qt, GLib, Cocoa) use their own event loop,
Swiften comes prepackaged with classes that integrate with these
event loops. These classes can be found in
<literal>Swiften/EventLoop</literal>.
In this example, however, we don't use such a framework,
so we use Swiften's own <literal>SimpleEventLoop</literal>. This
class is used by simply instantiating it at the beginning of the
application, and calling <literal>run()</literal> after everything
is set up, which will go into an infinite loop. Apart from constructing,
passing, and (if necessary) starting the event loop, you will probably
have no other contact with it in the rest of the application.
</para>
<para>
Another prerequisite of Swiften's classes is an implementation of
network interaction, provided through the
<literal>NetworkFactories</literal> class. Swiften comes with a
Boost-based network implementation, implemented in
<literal>BoostNetworkFactories</literal>. As with the event loop,
you probably will have no need to interact with this class apart
from constructing it.
</para>
<para>
Swiften's central class for implementing XMPP applications is
<literal>Client</literal>. This class handles all the interaction
with the XMPP network. After constructing it with the JID and
password with which we want to connect, we call
<literal>connect()</literal> to instruct the client to connect to
the XMPP server with the given credentials. Note that this call returns
immediately; it is only when starting the event loop that network
the actual connection process will start. The call to
<literal>setAlwaysTrustCertificates()</literal> before connecting
avoids checks for certificate validity; this is convenient for
the examples in this guide (because not all servers have trusted
certificates), but for production software, you should
not call this.
</para>
</sect1>
<sect1>
<title>Building EchoBot</title>
<para>
To build your application, you will need to set up your build
environment to use the correct include and library paths for
Swiften, and link against the Swiften library. This depends
on both the compiler you are using, and the flags you used to build
Swiften. To get the list of compiler options, Swiften comes with
a program <literal>swiften-config</literal> (located in
<literal>Swiften/Config</literal> in the Swiften tree). Calling this
with the <literal>--libs</literal> option will return the list of link flags,
whereas calling it with the <literal>--cflags</literal> option will return the
list of C(++) compiler flags.
</para>
</sect1>
<sect1>
<title>Reacting to events: Signals, Slots & Bind</title>
<para>
Up to this point, our client doesn't do anything useful. In this
section, we make the client react to XMPP events. The code can
be seen in <xref linkend="Example-EchoBot2"/>.
</para>
<example id="Example-EchoBot2">
<title>Reacting to events: Notify whenever the client is connected to the network, and echo back incoming messages</title>
<include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoBot2.cpp.xml" xpointer="xpointer(//programlisting|//calloutlist)"/>
</example>
<para>
A first thing we want to do is print out a message when the client
is connected to the server. Swiften uses the
<emphasis>signal/slot</emphasis> paradigm for notifying interested
parties of events. A <emphasis>signal</emphasis> is an object
representing a type of event. For example, <literal>Client</literal>
has an <literal>onConnected</literal> signal for notifying whenever
the client is connected to the network. If you are interested in
a particular signal, you connect a <emphasis>slot</emphasis> to the
signal. A slot represents a callback that will be called whenever a
signal is emitted. Since we want to print out a message whenever
we're connected to the network, we connect to the client's signal,
and tell it to call <literal>handleConnected</literal> (which prints
out a message):
<programlisting>client->onConnected.connect(&handleConnected)</programlisting>
</para>
<para>
Another event we're interested in is whenever a message comes in.
For this purpose, <literal>Client</literal> provides a signal called
<literal>onMessageReceived</literal>. The major difference with the
previous <literal>onConnected</literal> signal is that this signal
also can provide extra information to the callback: the actual
message received. A signal can provide this extra information through
one or more arguments, which will be passed to the slot's parameters.
To be able to handle parameters to slots, there needs to be a more
general representation of callbacks than just function pointers. This
is where Boost's <literal>bind</literal> comes in: <literal>bind</literal>
provides a way to construct <emphasis>functors</emphasis> (callbacks, slots, …), by combining function pointers and parameter values.
For example, to connect the signal to our slot, we call:
<programlisting>client->onMessageReceived.connect(bind(&handleMessageReceived, _1))</programlisting>
This is essentially saying: when the
<literal>onMessageReceived</literal> signal is emitted, call
<literal>handleMessageReceived</literal>, and pass it the first
parameter provided by the slot (which, in this case, is the actual
message received).
</para>
<para>
The implementation of <literal>handleMessageReceived</literal> should be straightforward: put the <emphasis>To</emphasis> address in place of the <emphasis>From</emphasis> address, and send the message to the server. One
thing to note is that <literal>Message::ref</literal> represents a
<emphasis>shared pointer</emphasis> to a <literal>Message</literal>
stanza. Shared pointers behave the same as regular pointers, except that,
when the last copy of the pointer goes away, the object it points to is
deleted as well. <literal>Message::ref</literal> is in fact a
typedef for <literal>boost::shared_ptr<Message></literal>.
Although Swiften tends to prefer the use of the <literal>::ref</literal>
notation, you will see both forms used intermixed.
</para>
<para>
If you use a C++ compiler that supports C++0x lambda expressions (such as
GCC 4.5 or Microsoft Visual Studio 2010), you can write this example in a more
concise way, as illustrated in <xref linkend="Example-EchoBot0x"/>. However,
for the remainder of this guide, we will not use this functionality.
</para>
<example id="Example-EchoBot0x">
<title>EchoBot using C++0x lambda expressions. This is currently only possible with a limited set of C++compilers.</title>
<include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoBot0x.cpp.xml" xpointer="xpointer(//programlisting|//calloutlist)"/>
</example>
<para>
Before moving on to the next step, we are going to rearrange our
code from <xref linkend="Example-EchoBot2"/> a bit, to make it a bit cleaner.
Instead of using global
variables, we are going to create an <literal>EchoBot</literal>
class with the current code in it. The resulting code can be found
in <xref linkend="Example-EchoBot3"/>.
</para>
<example id="Example-EchoBot3">
<title>Creating an EchoBot class</title>
<include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoBot3.cpp.xml" xpointer="xpointer(//programlisting|//calloutlist)"/>
</example>
<para>
The important thing to consider in this step are the changes to the signal
connections. Since we are now passing member variables of a class
to the signal, we need to use <literal>bind</literal> to pass
in the actual object on which this member variable is called as
the first parameter.
</para>
<para>
The only thing we added to this version is the
<literal>ClientXMLTracer</literal>. This class will dump all
incoming and outgoing XMPP messages to the console, which can be handy
for debugging our bot.
</para>
</sect1>
<sect1 id="Section-Requests">
<title>Presence Management: Requests</title>
<para>
The current version of our EchoBot does what it is supposed to do:
it answers all incoming messages. However, although users can add
the bot to their contact list, they will not see when it is
online, since the bot doesn't do any presence handling yet. In
this section, we explain the different steps involved in adding
presence management, resulting in the code in
<xref linkend="Example-EchoBot4"/>.
</para>
<example id="Example-EchoBot4">
<title>Adding presence management: Requesting the initial roster, and auto-approving incoming subscription requests.</title>
<include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoBot4.cpp.xml" xpointer="xpointer(//programlisting|//calloutlist)"/>
</example>
<para>
First of all, our bot needs to listen to incoming subscription requests
from users who want to add it to their roster, and automatically
approve them. This is done by connecting to
the <literal>onPresenceReceived</literal> signal, checking whether
the incoming presence is a subscription request, and if so,
respond to it with an approval (in <literal>handlePresenceReceived</literal>).
</para>
<para>
The first version of the XMPP protocol states that a client will not
get any presence
subscriptions until it requests the roster. To make sure we handle
this, we want to make sure our bot requests the roster at login.
After getting the <literal>onConnected</literal> signal, we
therefore send a
<emphasis>request</emphasis> to retrieve the roster. Swiften's
<literal>Request</literal> classes correspond to XMPP IQ Get or
Set actions. Swiften provides a set of built-in request classes for
the most common tasks in <literal>Swiften/Queries/Requests</literal>,
and can be easily extended to use add your own (see <xref linkend="Section-Extending"/>). Requests have an <literal>onResponse</literal> signal,
which is emitted when a response comes in. This signal has 2 parameters:
the actual response data (the <emphasis>Payload</emphasis>), and an
optional error payload in case there was an error executing the
request. To use a <literal>Request</literal> class, you construct
it with the correct parameters, connect to the
<literal>onResponse</literal> signal, and then send the request by
calling <literal>send()</literal> on it. In this case, we're not
interested in the actual payload of the response (passed as the
first parameter), so we pass it a slot with only the second parameter
(the error payload). When we get the roster back, we send initial
presence to all our subscribers, announcing them we're online.
</para>
</sect1>
<sect1 id="Section-Responders">
<title>Publishing version information: Responders</title>
<para>
Most XMPP clients have support for querying software version information
of a client through
<citation><biblioref linkend="XEP-0092"/></citation>. These clients
send out an IQ-Get request to an entity, which responds with the
requested information. We would like our bot to listen to these
requests, and respond with the correct information. Swiften uses
<literal>Responder</literal> classes for the purpose of responding
to IQ requests, and are therefore the dual of the
<literal>Request</literal> clients discussed in the previous section.
</para>
<example id="Example-EchoBot5">
<title>Adding presence management: Requesting the initial roster, and auto-approving incoming subscription requests.</title>
<include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoBot5.cpp.xml" xpointer="xpointer(//programlisting|//calloutlist)"/>
</example>
<para>
Using <literal>SoftwareVersionResponder</literal> is pretty
straightforward, as can be seen in <xref linkend="Example-EchoBot5"/>:
simply construct the responder, set the correct
parameters, call <literal>start()</literal>, and it will automatically respond to
the incoming
requests. Other <literal>Responder</literal> classes may provide
signals to notify of incoming requests, or may have some other
behavior. For a detailed explanation of responders, see
<xref linkend="Section-CustomQueries"/>.
</para>
</sect1>
<sect1 id="Section-Extending">
<title>Extending Swiften with new payloads: Payloads, Parsers, and Serializers</title>
<para>
Swiften uses abstract datastructures for all the data that is received
and sent over the XMPP network. The declaration of these datastructures
can all be found in <literal>Swiften/Elements</literal>. For
representing the XMPP stanzas, Swiften uses the
<literal>Message</literal>, <literal>Presence</literal>, and
<literal>IQ</literal> classes. Each stanza can have an arbitrary
amount of child <emphasis>payloads</emphasis>, represented by the
<literal>Payload</literal> class. A payload typically corresponds
to a (namespaced) child XML element of a stanza; for example, the
<literal><query xmlns="jabber:iq:roster"/></literal>
element used for managing the roster is represented as a
<literal>RosterPayload</literal>.
</para>
<para>
If you want to extend Swiften with your own XMPP extension, you will first
need to create a payload for this extension. For example, suppose we want to
reate an extension for use in our Echo bot that contains a special textual
message, and add this to all our outgoing messages,
we create the <literal>EchoPayload</literal> illustrated in
<xref linkend="Example-EchoPayload"/>. We can then append or retrieve this
payload from the stanzas using <literal>Stanza::getPayload()</literal> and
<literal>Stanza::addPayload()</literal>. For example, the version of our
bot in <xref linkend="Example-EchoBot6"/> checks whether an incoming
message contains the <literal>EchoPayload</literal>, and if not,
echoes back the message, and adds an extension to the message with a
descriptive text.
</para>
<example id="Example-EchoPayload">
<title>Extending Swiften with a new payload: <literal>EchoPayload</literal></title>
<include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoPayload.h.xml" xpointer="xpointer(//programlisting|//calloutlist)"/>
</example>
<example id="Example-EchoBot6">
<title>Adding a custom extension: Using a custom element, and registering a parser (factory) and serializer for the element.</title>
<include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoBot6.cpp.xml" xpointer="xpointer(//programlisting|//calloutlist)"/>
</example>
<para>
However, having the element is not enough; Swiften also needs to know how to
extract this payload from the incoming stanzas, and know how to send it on
outgoing stanzas. In order to do this, Swiften uses XML parsers and serializers
for the payload. We therefore need to create a parser and serializer for our
new payload, and register it with <literal>Client</literal>. Serializers are
implemented as subclasses from <literal>PayloadSerializer</literal>, and provide
the basic methods <literal>canSerialize()</literal> and
<literal>serialize()</literal>. The serializer
is registered using <literal>Client::addPayloadSerializer()</literal>
(and unregistered using <literal>Client::removePayloadSerializer()</literal>).
Parsers consist of 2 parts: a subclass of <literal>PayloadParser</literal>, which
parses incoming XML in an event-driven way and builds up the payload, and
a subclass of <literal>PayloadParserFactory</literal>, which is responsible
for detecting whether a given parser can parse an incoming element, and
creates a parser. The parser factory is registered with the client using
<literal>Client::addPayloadParserFactory()</literal> (and unregistered
using <literal>Client::removePayloadParserFactory()</literal>).
</para>
<para>
Although you can subclass the base classes for parsers and serializers
directly, Swiften comes with utility classes that contain common
functionality for parsers and serializers. For example, for our EchoBot,
the parser and serializer using these utility classes is shown in
<xref linkend="Example-EchoPayloadParser"/> and
<xref linkend="Example-EchoPayloadSerializer"/> respectively. Registration
of the parser and serializer is shown in the constructor of our EchoBot in
<xref linkend="Example-EchoBot6"/>.
</para>
<example id="Example-EchoPayloadParser">
<title>The parser and parser factory for <literal>EchoPayload</literal></title>
<include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoPayloadParserFactory.h.xml" xpointer="xpointer(//programlisting|//calloutlist)"/>
</example>
<example id="Example-EchoPayloadSerializer">
<title>The serializer for <literal>EchoPayload</literal></title>
<include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoPayloadSerializer.h.xml" xpointer="xpointer(//programlisting|//calloutlist)"/>
</example>
<para>
If you want to create your own parser and serializers, you can look at the
built-in parsers and serializers in the Swiften library, located in
<literal>Swiften/Parser/PayloadParsers</literal> and <literal>Swiften/Serializer/PayloadSerializers</literal>.
</para>
</sect1>
<sect1 id="Section-CustomQueries">
<title>Extending Swiften with new queries and responders</title>
<para>
<xref linkend="Section-Requests"/> and <xref linkend="Section-Responders"/>
explained that Swiften provides Requests and Responder classes for querying
or responding to queries of specific payloads. If you extend Swiften with your
own payloads, you can use these to create your own <literal>Request</literal>
or <literal>Responder</literal> subclasses. Swiften also provides convenience
classes such as <literal>GenericRequest</literal>, <literal>GetResponder</literal>
and <literal>SetResponder</literal> for creating your requests and responders
for your custom payloads.
</para>
</sect1>
<sect1>
<title>Using Swiften's convenience classes</title>
<para>
Swiften comes with flavors of the Client class: <literal>CoreClient</literal>, which
implements only the basics of connecting to the XMPP server, without any built-in
responders. If you want to build a client from scratch, this class is probably your
preferred Swiften interface. However, most users of Swiften will not want to bother with
explicitly instantiating responders to basic functionality such as software version
information etc., and will want to have the convenience of built-in responders and
utility classes. In this case, you can use the <literal>Client</literal> class (a
subclass of <literal>CoreClient</literal>, which
implements most of the common XMPP client functionality, including roster and subscription
management, VCards, Avatars, Service Discovery, Multi-User Chats, ...
</para>
</sect1>
<sect1>
<title>Writing server components</title>
<para>
Swiften also provides classes for creating server components. The
<literal>Component</literal> class has a similar interface
as <literal>Client</literal>, but uses the component protocol to connect with
the XMPP server. Except for a few classes, the same techniques and classes for
writing clients can be applied to write components.
<xref linkend="Example-EchoComponent"/> illustrates how we would build a
component version of our Echo bot.
</para>
<example id="Example-EchoComponent">
<title>EchoBot as a server component</title>
<include xmlns="http://www.w3.org/2001/XInclude" href="Examples/EchoBot/EchoComponent.cpp.xml" xpointer="xpointer(//programlisting|//calloutlist)"/>
</example>
</sect1>
</chapter>
<bibliography>
<title>Bibliography</title>
<biblioentry id="XMPP-TDG">
<abbrev>XMPP-TDG</abbrev>
<title><ulink url="http://oreilly.com/catalog/9780596157197/">XMPP: The
Definitive Guide</ulink></title>
<author>
<firstname>Peter</firstname>
<surname>Saint-Andre</surname>
</author>
<author>
<firstname>Kevin</firstname>
<surname>Smith</surname>
</author>
<author>
<firstname>Remko</firstname>
<surname>Tronçon</surname>
</author>
</biblioentry>
<biblioentry id='XEP-0092'>
<abbrev>XEP-0092</abbrev>
<title><ulink url='http://www.xmpp.org/extensions/xep-0092.html'>Software Version</ulink></title>
<author>
<firstname>Peter</firstname>
<surname>Saint-Andre</surname>
</author>
</biblioentry>
</bibliography>
</book>
|