jabber developer’s handbook [sams 2004]

504

Upload: kampring-pisan

Post on 07-Apr-2015

461 views

Category:

Documents


12 download

DESCRIPTION

Jabber Developer’s Handbook [Sams

TRANSCRIPT

Page 1: Jabber Developer’s Handbook [Sams 2004]
Page 2: Jabber Developer’s Handbook [Sams 2004]

JabberD E V E L O P E R ’ S H A N D B O O K

00 0672325365 FM 1/21/05 2:22 PM Page i

Page 3: Jabber Developer’s Handbook [Sams 2004]

00 0672325365 FM 1/21/05 2:22 PM Page ii

Page 4: Jabber Developer’s Handbook [Sams 2004]

Jabber

Sams Publishing, 800 East 96th Street, Indianapolis, Indiana 46240

DEVELOPER’S LIBRARY

D E V E L O P E R ’ S H A N D B O O K

Dana MooreWilliam Wright

00 0672325365 FM 1/21/05 2:22 PM Page iii

Page 5: Jabber Developer’s Handbook [Sams 2004]

Jabber Developer’s HandbookCopyright © 2004 by Sams Publishing

All rights reserved. No part of this book shall be reproduced, storedin a retrieval system, or transmitted by any means, electronic,mechanical, photocopying, recording, or otherwise, without writtenpermission from the publisher. No patent liability is assumed withrespect to the use of the information contained herein.Althoughevery precaution has been taken in the preparation of this book, thepublisher and author assume no responsibility for errors or omis-sions. Nor is any liability assumed for damages resulting from the useof the information contained herein.

International Standard Book Number: 0-672-32536-5

Library of Congress Catalog Card Number: 752063325360

Printed in the United States of America

First Printing: July 2003

06 05 04 03 4 3 2 1

TrademarksAll terms mentioned in this book that are known to be trademarksor service marks have been appropriately capitalized. SamsPublishing cannot attest to the accuracy of this information. Use of aterm in this book should not be regarded as affecting the validity ofany trademark or service mark.

Warning and DisclaimerEvery effort has been made to make this book as complete and asaccurate as possible, but no warranty or fitness is implied.The infor-mation provided is on an “as is” basis.

Bulk SalesSams Publishing offers excellent discounts on this book whenordered in quantity for bulk purchases or special sales. For moreinformation, please contact:

U.S. Corporate and Government [email protected]

For sales outside of the U.S., please contact:

International [email protected]

Acquisitions EditorKathryn Mohr

Development EditorScott Meyers

Managing EditorCharlotte Clapp

Project EditorGeorge E. Nedeff

Copy EditorMargo Catts

IndexerErika Millen

ProofreaderTracy Donhardt

Technical EditorRyan Eatman

Team CoordinatorVanessa Evans

Multimedia DeveloperDan Scherf

DesignerGary Adair

Page LayoutBronkella Publishing

00 0672325365 FM 1/21/05 2:22 PM Page iv

Page 6: Jabber Developer’s Handbook [Sams 2004]

For my wife, Karan, with appreciation for your tolerance of all those days and nights I spent holed up working

on this book. —WW

For Walter J.Yourell, who is for me the greatest member ofthe “Greatest Generation,” and for my wife, Jane, whoselong-suffering forbearance and feigned interest in arcanepoints of Jabber architecture explained to her in the wee

hours are duly noted and cherished. —DM

00 0672325365 FM 1/21/05 2:22 PM Page v

Page 7: Jabber Developer’s Handbook [Sams 2004]

Contents at a Glance

I Jabber In-Depth

1 What Is Jabber Technology? 7

2 Installing and Configuring Jabber Software 35

3 All About Jabber Clients 69

4 Jabber Server Architecture 123

5 Extending the Jabber Server 155

6 Jabber Security 225

II Jabber-Based Networked Applications

7 Jabber and Web Services 273

8 Jabber and Conversational Software Agents 299

9 Jabber and System Control and Administration 327

10 Jabber and JXTA 365

11 Jabber Libraries for Popular Languages 399

III Appendixes

A Glossary 431

B XML Basics 435

C Resources 439

Index 443

00 0672325365 FM 1/21/05 2:22 PM Page vi

Page 8: Jabber Developer’s Handbook [Sams 2004]

Table of Contents

1 Introduction: Let’s Jabber 1Jabber Enables Peer-to-Peer Computing 1Jabber Enables an Evolved System Architecture 2

I Jabber In-Depth 5

1 What Is Jabber Technology? 7Traditional Applications and How They Got That Way 7The Nature of Traditional Tools 8Shortcomings of Traditional Tools 9Jabber Is… 11

…Built-in Services… 11Service Discovery 13Server and Client Interactions 14…Client-Based Services… 17…Open… 19…Asynchronous… 19…Extensible… 20…Decentralized… 21…Secure… 22…XML Protocol… 22Riding on TCP/IP 22Provides Real-time Exchange of Messages 22…And Presence Information 23…Between Two Endpoints on the Open Internet 23…And a Corporate Intranet 24

A Useful Application to Jump-Start Your Interest 24A Practical User Creation Script (Java version) 25A Practical User Creation Script (Python version) 30Jabber’s Open Source Development Heritage(And Its Implications) 33

Conclusion (or Rather,The Beginning) 33

00 0672325365 FM 1/21/05 2:22 PM Page vii

Page 9: Jabber Developer’s Handbook [Sams 2004]

viii Contents

2 Installing and Configuring Jabber Software 35Downloading the Server Software 35Installing the Server Software 36

Linux and Unix 36Windows 39

Initial Server Configuration 40Jabberd Command-line Arguments 40The jabber.xml File 40Starting jabberd 43

Service Configuration Details 44Service XDB 44Service io 46Service c2s 48Service s2s 49Service dnsrv 51Log Services 51The Sessions Service 53

Common Optional Services 61Jabber User Directory (JUD) 61Conferencing 63

Instant Messaging Clients 66Summary 68

3 All About Jabber Clients 69What Is a Jabber Client? 70Session Mechanics 70Protocol Mechanics 70Protocol Details 78

The <stream:stream>Tag 78The <iq> Element 78

Jabber Presence 84Presence Attributes 87The <presence> Element 87The <message> Element 91The <body/> Subelement 93Additional <message/> Subelements 95

00 0672325365 FM 1/21/05 2:22 PM Page viii

Page 10: Jabber Developer’s Handbook [Sams 2004]

ixContents

Using <message/> to Convey Arbitrary Data 99

Summary 122

4 Jabber Server Architecture 123High-Level Architecture 123Messages and Sessions 125

Remote Messaging 127Client Initialization 130

Browsable Agents 142Instant Messaging Gateways 148Summary 153

5 Extending the Jabber Server 155A Database Service 156

Configuring the Service 156Code Listing 158Connecting to the Server 166JDBC Survival Skills 167Browsing 169Searching 174

A Report Service 179Code Listing 180Registration 186A Small Digression:A Custom Packet Type forJabberBeans 188Custom Storage in XDB 193Timed Report Generation 196

An Inventory Management Service 198Decomposing the Inventory Management Service 208Summary 224

6 Jabber Security 225Client Registration 226

Disabling Automatic Registration 231Registration Data 232

00 0672325365 FM 1/21/05 2:22 PM Page ix

Page 11: Jabber Developer’s Handbook [Sams 2004]

x Contents

Client Authentication 233Plain Authentication 236Digest Authentication 238Zero-Knowledge Authentication 242

A Custom Authentication Component 245Connecting the Authentication Service 246Handling Authorization Packets 247

Using SSL for Client Connections 258SSL Overview 258Enabling SSL in the Jabber Server 260

Server-to-Server Connection Authentication (Dialback) 265Summary 268

II Jabber-Based Networked Applications 271

7 What’s in a Name:Web Services 273First-Generation Applications: Servers and GlassTerminals 273Second-Generation Applications: Servers and Clients 274Third Generation Applications: Enter the Web 275Fourth-Generation Applications: XML and WebServices 276

XML-RPC 278SOAP 283WSDL 284UDDI 285

Jabber and XML-RPC 286Jabber-Based RPC 287

Jabber RPC Invoker 287Jabber RPC Service Provider (First Version) 289Jabber RPC Service Provider (Second Version)292

Jabber-RPC Object Lessons 296Summary 296

00 0672325365 FM 1/21/05 2:22 PM Page x

Page 12: Jabber Developer’s Handbook [Sams 2004]

xiContents

8 Jabber and Conversational Software Agents 299Motivating This Application 300Accomplishing the Objective 300What Is Alice? 300Why Alice Appears to Work 301Alice Design Principles 301The Alicebot Server 302

AIML Folder—Alice’s Built-in Knowledge 302Lib Folder—Alice ProgramD’s Java Library 303Alice Configuration Files 303

ALICE Static Knowledge (AIML) Files 306The Category Tag 306The Pattern Tag 306The Template Tag 306The Topic Tag 306

Fitting the Pieces Together 316The AliceJabber Mux Code 318Running ALICE with Jabber 324Summary 325

9 Jabber and System Control andAdministration 327Jabber for System Event Monitoring 327Jabber for Version Management 338Jabber for Distributed Control 341Jabber for Application Monitoring 350Summary 363

10 Jabber and JXTA 365JXTA Technology Introduction 365Elements of JXTA Technology 366

Identifiers 366Advertisements 366Messages 368Peers 368Peer Groups 369

00 0672325365 FM 1/21/05 2:22 PM Page xi

Page 13: Jabber Developer’s Handbook [Sams 2004]

xii Contents

Pipes 369Modules (Services) 369

Roles for JXTA Peers 370Rendezvous Peers 370Relay Peers 370Proxy Peers 370

Trying Out JXTA 370The JXTA Java Binding API 375Example:A Jabber-to-JXTA Bridge 376

Details of the talk Protocol 379Summary: Jabber and JXTA as ComplimentaryTechnologies 397

11 Jabber Libraries for Popular Languages 399Jabber-Net—Jabber for the .NET Environment 399

Building the Examples 407iksemel—Jabber for C/C++ 408JabberBeans—Jabber for Java 413JabberPy—Jabber for Python 414A Cross-Language Example 414

Jabberlib—Jabber for TCL 418Net::Jabber—Jabber for Perl 421Jabber4R—Jabber for Ruby 424

Summary 427

III Appendixes 429

A Glossary 431

B XML Basics 435A Simple Document 435Tag Attributes 436Comments 436XML Namespaces 437XML Streams 438

00 0672325365 FM 1/21/05 2:22 PM Page xii

Page 14: Jabber Developer’s Handbook [Sams 2004]

xiiiContents

C Resources 439Jabber Servers 439Jabber Protocol 439Jabber Clients 439Jabber Libraries 440

Macromedia Flash 440JabberBeans 440Alicebots 440JabberPy 440

Miscellany 441

Index 443

00 0672325365 FM 1/21/05 2:22 PM Page xiii

Page 15: Jabber Developer’s Handbook [Sams 2004]

About the AuthorsDana Moore is a Senior Scientist with BBN Technologies inArlington,VA. He joined BBN in June 2001 to focus onULTRA*LOG, a DARPA initiative to build very large-scale Java-based multi-agent societies. Previously, he was Chief Scientist withRoku Technologies, a P2P infrastructure developer, and prior to that, aDistinguished Member of Technical Staff at AT&T LaboratoriesResearch. He is the coauthor of Peer-to-Peer: Building Secure, Scalable,

and Manageable Networks. He is a popular conference speaker on software agent systemsand various management topics, a university lecturer, and he has contributed articles fornumerous computing publications. Moore holds a master of science degree inTechnology Management from the University of Maryland, and a bachelor of science inIndustrial Design, also from the University of Maryland.

William Wright is a Division Engineer with BBN Technologies inArlington,VA. He provides architecture design and development sup-port for several projects utilizing the Cognitive Agent Architecture(Cougaar) distributed software agent framework. He led the integra-tion and demonstration of one of the world’s largest software agentsystems, and led the development of an extension to Cougaar to bringagent technology to embedded systems. He has recently written for

Java Developer’s Journal, Dr. Dobb’s Journal, and Embedded Systems Programming magazines.He is coauthor of the book Beginning Java Networking.Wright holds an M.S. in computerscience from George Mason University and a Bachelor of Music Education from IndianaUniversity.

00 0672325365 FM 1/21/05 2:22 PM Page xiv

Page 16: Jabber Developer’s Handbook [Sams 2004]

AcknowledgmentsThe authors would like to thank our colleague, Rich Kilmer, the author of the RubyJabber library, for introducing us to Jabber.We also thank the editors and reviewers fortheir invaluable help in making this book clear, correct, and readable.

We would also like to acknowledge Dr. Mark Greaves and the DARPA UltralogProgram for giving us a testbed for trying out ideas that materially informed both ourphilosophy and technical approach.

00 0672325365 FM 1/21/05 2:22 PM Page xv

Page 17: Jabber Developer’s Handbook [Sams 2004]

We Want to Hear from You!As the reader of this book, you are our most important critic and commentator.We valueyour opinion and want to know what we’re doing right, what we could do better, whatareas you’d like to see us publish in, and any other words of wisdom you’re willing topass our way.

You can email or write me directly to let me know what you did or didn’t like aboutthis book—as well as what we can do to make our books stronger.

Please note that I cannot help you with technical problems related to the topic of this book, andthat because of the high volume of mail I receive, I might not be able to reply to every message.

When you write, please be sure to include this book’s title and author, as well as yourname and phone or email address. I will carefully review your comments and share themwith the author and editors who worked on the book.

Email: [email protected]: Mark Taber

Associate PublisherSams Publishing800 East 96th StreetIndianapolis, IN 46240 USA

Reader ServicesFor more information about this book or others from Sams Publishing, visit our Web siteat www.samspublishing.com.Type the ISBN (excluding hyphens) or the title of the bookin the Search box to find the book you’re looking for.

00 0672325365 FM 1/21/05 2:22 PM Page xvi

Page 18: Jabber Developer’s Handbook [Sams 2004]

Introduction: Let’s Jabber

“First principles, Clarice. Simplicity. Read Marcus Aurelius. Of each particular thing, ask, ‘What isit, in itself, what is its nature…?’”

Dr. Hannibal Lecter, Silence of the Lambs

IN LOOKING AT HOW WE COULD BEST SERVE our readers, we had to decide whether toask you to spend your time wading through the detail of what particular messages looklike (and there are many of them) or to focus more on interesting applications. Becausethe details of message formats are well-documented elsewhere (see Appendixes A and Cfor resources), and because the debugger capabilities incorporated in many of the freelyavailable IM-style Jabber clients accurately show the XML structure of messages sent andreceived, we felt that your interests as a developer would be best served if we would giveyou more interesting examples with lots of working code that you could build on.

As developers ourselves, we felt that one good example is worth a thousand words of protocol specification.That is not to say that we won’t cover message structure andprotocol—we will. However, we learned Jabber by constructing model applications andobserving how they worked, and we suspect that most other developers work this way,too. Frankly, it’s more leverageable to learn the high-level APIs in the JabberBeans (Java)and JabberPy (Python) libraries than to know the precise XML structure of every poten-tial message and message type.

Jabber Enables Peer-to-Peer ComputingWe will also cover the open-source Jabber server and how it works.Although Jabbermakes use of a server between clients, we tend to think of it as enabling an inherentlypeer-to-peer (P2P) application model.

In truth, P2P can take many shapes, some of which have servers in the Net, and someof which don’t. Jabber is, we maintain, very much a way to create P2P applications. Inone of the applications we show in Chapter 3,“Jabber Client Protocol,” for example, weshow you a P2P style application for photo and MP3 sharing. In Chapter 7,“Jabber andWeb Services,” we offer code that shows off Jabber’s capability to tunnel firewalls.

01 0672325365 Intro 1/21/05 2:22 PM Page 1

Page 19: Jabber Developer’s Handbook [Sams 2004]

2 Introduction

With its use of Jabber Identifiers as an alternate address space, its capability to tran-scend firewalls, and its capability to queue messages and create long-lived sessions, Jabberis the very essence of P2P services.

In this book, we are specifically interested in presenting Jabber as a P2P protocol forunifying the various pieces of distributed applications and providing applications thatdemonstrate a preferred way to create architectural patterns such as software agents andWeb services.

Jabber Enables an Evolved System ArchitectureSoftware paradigms tend to move much more slowly than we think.The World WideWeb, for example, ignored the vast computing power at the end of a network connec-tion, turning the personal computer back into a glass terminal, recapitulating for usersthe ancient experience of the old 3270 style bi-synchronous terminal.Although theimplementation was worlds apart from mainframe computing, the users’ experience hadmuch the same feel.

When we think about systems, especially distributed systems 10, 20, 50 years hence,we might therefore imagine that things may be very different, or not change much really.

In the last 50 years, we have evolved from single large monolithic systems to looselycoupled architectures that are systems of systems. One genuinely new model has beenthe notion of “conversational systems,” in which software modules exchange messageswith one another. Sometimes the content of these messages may be intended for humanparticipants and sometimes not.What we think of as program modules may start to looklike software agents, with request/response semantics transported as messages.

Therefore, it may be that conversational systems, transporting richly encoded contentsbetween software elements, offer the most elegant and robust mechanism for solving thetoughest programming challenge we face today: constructing robust systems of systems.This may still be true even years from now.A single software process may contain justenough functionality to either service requests from other processes or make requestsfrom other processes.

Successful software may therefore involve knitting together the capabilities of bits ofcode written not as a part of some narrowly focused application but as a contributor to aglobal software conversation. Every program will exist in a network fabric of other soft-ware from which it will get significant parts of its functionality.

It is an underlying theme of this book, then, that future systems of systems willindeed be based on a conversational, message-based transport architecture such as Jabberbecause all network endpoints will look like loosely coupled, highly autonomous, dis-tributed computational entities with conversation-like syntactic/semantic capabilities.Youwill find concrete examples of computational entities engaged in conversational relation-ships throughout the sample applications in the book.

01 0672325365 Intro 1/21/05 2:22 PM Page 2

Page 20: Jabber Developer’s Handbook [Sams 2004]

3Introduction

It has been said that knowledge is what makes the difference between the world youwant to create and the world other people think you should have. In this book we com-bine a healthy dose of knowledge with concrete examples you can use to jump startyour own efforts.

In Chapter 5,“Extending the Jabber Server,” for example, we show information“push” services implemented with Jabber. In Chapter 8,“Jabber and ConversationalSoftware Agents,” we demonstrate a highly conversational application in which Jabber isfundamental in creating a chat application between a human user and a software agent.In Chapter 11,“Jabber Libraries for Popular Languages,” we demonstrate how Jabberworks to extend distributed Internet service architectures. In short, throughout thisbook, you will find helpful and instructive examples of applications that make use of thisnew paradigm that you can use as sources of ideas, design elements, and code.

We believe that in using the techniques and approaches to application-building wedescribe, you will be well along a path to building robust and scalable applications and havethe knowledge to build a world of applications limited only by your own imagination.

01 0672325365 Intro 1/21/05 2:22 PM Page 3

Page 21: Jabber Developer’s Handbook [Sams 2004]

01 0672325365 Intro 1/21/05 2:22 PM Page 4

Page 22: Jabber Developer’s Handbook [Sams 2004]

I Jabber In-Depth

1 What is Jabber Technology?

2 Installing and Configuring Jabber Software

3 Jabber Client Protocol

4 Jabber Server Architecture

5 Extending the Jabber Server

6 Jabber Security

02 0672325365 Pt 1 1/21/05 2:22 PM Page 5

Page 23: Jabber Developer’s Handbook [Sams 2004]

02 0672325365 Pt 1 1/21/05 2:22 PM Page 6

Page 24: Jabber Developer’s Handbook [Sams 2004]

1What Is Jabber Technology?

Why did we think we could start clean-slated? The hardest to learn is the least complicated.

“Least Complicated,” The Indigo Girls

FIRST THINGS FIRST.THIS IS A BOOK FOR SOFTWARE developers.As developers, we mustadmit that we really can’t start clean-slated in embracing a different paradigm than theone in which we are used to programming.We have a certain world view that has beeninfluenced by over two decades of Windows and Macintosh programming and designguidelines.We can not deny that our world view channels our thinking about applica-tions—what they do and what it means to create an application.This book gives youenough knowledge to help you write conversational applications.These are applications thatsit on a user’s desktop or on servers on the Net and transcend some of the limitations oftraditional software tools, applications that invite a user to interact in a newer and morenatural way.

Before we can really define Jabber and describe its architecture and show some inter-esting ways to use it, we should do a quick examination of the strengths and shortcom-ings of traditional tools and then talk about how applications architected with Jabber canaddress these shortcomings. It’s frankly the best way for you to get into the proper mind-set for writing the kind of applications you’ll be seeing in this book, so let’s get started.

Traditional Applications and How They GotThat WayGiven our developer biases, we tend to think of applications from two perspectives: asdata manipulation tools, and as substitutes for forms, pencil, and paper. From the first

03 0672325365 CH01 1/21/05 2:22 PM Page 7

Page 25: Jabber Developer’s Handbook [Sams 2004]

8 Chapter 1 What Is Jabber Technology?

perspective, we recognize that when we create a desktop-style application for a user, weare first and foremost enabling them to interact with a persistent store of some sort.Whether the store contains text and formatting data or something else is immaterial,really.The canonical application’s focus is about creating and manipulating a database ofsome sort. Even when we move the application to the Internet, we are still thinking interms of transacting against a data store through a middleware layer.

From the second perspective—replacing forms, pencil, and paper with visual fix-tures—we create a desktop application that is a collection of two-dimensional represen-tations meant to represent either physical objects, such as buttons that can be pushed, orfeatures on paper forms, such as check boxes and text entry areas.

Historically, applications offer a certain look and feel because they were initiallyintended as direct replacements for paper or mechanical artifacts.Their design centercomes from serving the needs of an era of population explosion at the beginning of thetwentieth century where meta-data about people had to be reduced to numbers thatcould be entered by a human clerk onto a paper form, making automation work for themasses of humanity.

The Nature of Traditional ToolsThe applications we create today owe much to this lineage.We have combined andsmoothed the data entry and automation steps, without fundamentally changing the wayour application users interact with automation [1].

As a result, the applications we tend to create either for our corporate intranets or forexternal sale implement these patterns:

n Direct manipulation.We expect the application’s consumer to use a combina-tion of mouse, keyboard, menus (or equivalent keyboard shortcuts,) on-screen toolrepresentations, and a working area.This working area may contain text or digitalphotos; it really doesn’t matter.

n Immediate feedback. If an application does not faithfully echo back the charac-ter that was just typed, or display a change in the contents of the picture on whichthe spray-paint tool was just used, within very tight time constraints, users becomevery disenchanted with—even distrustful of—the application.

n Impersonal. Consumers should be able to walk up to any PC hosting the tool,and rely on their (often hard-won) training to manipulate the tool in the sameway every time.

1 We’re not suggesting that this is a bad thing. For example, writing books today is considerablyeasier than in the old typewriter era, given the capability to convert WYSIWYG editing prettymuch directly into typesetting commands.

03 0672325365 CH01 1/21/05 2:22 PM Page 8

Page 26: Jabber Developer’s Handbook [Sams 2004]

9The Nature of Traditional Tools

n Non-coordinative.Tools are rarely expected to act in concert with other tools toachieve some higher use goal.There are seeming exceptions that probe this rule,most notably the Unix convention of chaining tools through pipes.You can set upa multi-party meeting with Microsoft’s Outlook or the like, but we would definePIM tools and tool suites such as Microsoft Office as very large tools rather thanopen, communicative tools.

Shortcomings of Traditional ToolsHowever, we should be aware that applications have some less desirable anti-patterns[2]as well:

n They are generally “non-delegated.” That is, it is very hard to instruct a toolor a session-oriented Web page to do something for future use or on a continuingbasis.The tool generally has to be opened and in active use by the human to dosomething useful for the human.There’s no background mode for most tools.

n Tools are generally “islands of automation.” They usually have no coordina-tive capability:Alice uses a tool to send Bob a text document via email, butalthough Alice later revises the document several times, there’s no contextual toolthat notices that Alice has forgotten to email Bob any of the revisions.

n Tools lack a natural model for initiating dialog with a human in a waythat can be tolerated by the human. We all curse the notorious “paper clip”model for the help system delivered with a certain tools suite, but are there otherways to make a human aware that some new information is available, but that thehuman can choose to ignore for the time being?

n Tools don’t learn much from interactions with a human user. In a famousstudy at Xerox PARC, researcher Lucy Suchman was trying to figure out whetherit would be possible for copier machines to become smarter about what a humanuser wanted. She concluded in the short-duration sessions between a user and themachine that very little information of value was communicated to the machine;that the machine lacked sufficient vocabulary to understand user intent; and that“mutual constitutions of humans and artifacts do not occur in any singular timeand place, nor do they create fixed human/artifact relations.” In short, the domainof a given tool is so fixed and narrow that it can’t likely get enough time with ahuman to deliver much enduring value.

2 An AntiPattern is a software pattern that defines going from a problem to a bad solution. Badsolutions may be widely embraced for a variety of reasons—anything from dominance of a majorindustry proponent to designing around hardware assumptions that are no longer valid. BecauseAntiPatterns are often so embedded in our thinking, they often are not even recognized.

03 0672325365 CH01 1/21/05 2:22 PM Page 9

Page 27: Jabber Developer’s Handbook [Sams 2004]

10 Chapter 1 What Is Jabber Technology?

A large reason for these anti-patterns is that it’s hard to build tools with the vocabularyto perform services for us that we could conceivably ask another human to do for us.

Suppose that we could conceive of an application architecture that could addresssome of these serious anti-patterns. Suppose that we could develop to a standard that

n Is platform- and language-agnostic.n Is message-based, expressed in an open standard such as XML, and features send-

receive acknowledgement.n Can act as a bearer channel for file transfers and executable commands.n Is asynchronous, featuring store and forward capability and in-order message queu-

ing, and fostering a deferred interaction/indeterminate duration metaphor forapplications[4].

n Offers a service architecture through both client connections and pluggable com-ponents (where pluggable components can be close-to-the-metal C or a light-weight language such as Python).

n Offers a service architecture that is Network Address Translation (NAT)–friendlyand can carry XML-RPC and SOAP style messages.

n Enables the easy creation of user interfaces through an instant messaging interfaceor through custom clients, again with “platform plasticity”: It is O/S-agnostic andmultiple implementation language–friendly.

TipIn this book, the examples are shown mostly in Java and Python, both because those are the languages we

are most familiar with, and because they are two of the most robust and mature libraries available for

Jabber. If you email us with questions about the examples or other relevant topics, we will be most able to

help you in those languages but, as we mentioned earlier, dedicated people have written libraries for other

languages (including Perl, Macromedia Flash, C#, PHP, Visual Basic, and C++).

With such an architecture and supporting libraries you could create applications that acton the user’s behalf to accomplish goals on a deferred or delegated basis, or alert the userif he is online when something of interest happens (or wait until the user became avail-able and inform him then).Applications could become more conversational, could pres-ent a friendlier veneer.

An architecture could help us as developers to create applications that are not trappedby current firewall restrictions of typical HTTP-based client/server architectures, thatcould allow, for example, an application acting on a user’s behalf to reach back to thatuser on a client trapped behind a firewall.

3 Suchman, Lucy (1987). Plans and Situated Actions:The Problem of Human-MachineCommunication. Cambridge: Cambridge University Press.4 Deferred interaction and long duration are two of the defining hallmarks for agent-basedapplications.We will be showing you how to build applications in which an agent operates tire-lessly on the user’s behalf until instructed to terminate.

03 0672325365 CH01 1/21/05 2:22 PM Page 10

Page 28: Jabber Developer’s Handbook [Sams 2004]

11Shortcomings of Traditional Tools

Now for the surprise of the day: Jabber can help you achieve all these application par-adigms. Let’s now drill down into Jabber Technology and talk about how you can dothese things.

Jabber Is…Jabber enables you to provide built-in or client-based services based on an open,

asynchronous, extensible, decentralized, and secure XML protocol riding directly onTCP/IP to provide real-time exchange of messages and presence information betweentwo endpoints on the open Internet or between the open Internet and a corporateintranet.Whew! Quite a mouthful, wasn’t it? Let us pick that statement to bits andexplain a bit more fully.

…Built-in Services…A Jabber server is the combination of a message switch and service backplane, whichhosts slots for plugin components.There are three ways to fill a plugin slot in the Jabberbackplane and a fourth way to offer a service that looks like an ordinary client, exploredin the next section and shown in Figure 1.1.A server is responsible for providing the fol-lowing minimal set of services:

n Handling client connections and communicating directly with Jabber clientsn Communicating with other Jabber servers to give clients homed on other servers

seamless access to clients homed on this servern Acting as a “plugin manager” to manage and pass messages between components

loaded with the server

The Jabber server provides these services and will be the basis on which we build theapplications in the book.

Library Modules

First, you can create a component by creating a library module and then linking itdirectly into a Jabber server.When the server is started/restarted, the service becomesavailable to clients and there is a protocol for presenting loaded services to clients.Theseare shown in Figure 1 as “lib modules.”They are intimately entwined with the serverand incur the least run-time overhead of the service connection methods—at theexpense of requiring development in a C-language environment.

TCP/IP Sockets

For those of us who have sworn off programming in C except in dire emergen cies, it isalso possible to install a service across a TCP/IP socket connection and speak back andforth to the Jabber server that way. If you want to write in Java or Python or Perl, you’refree to do it that way.As long as you write the correct XML messages back and forth,the server considers the component “plugged in.”A nice advantage here is that you can

03 0672325365 CH01 1/21/05 2:22 PM Page 11

Page 29: Jabber Developer’s Handbook [Sams 2004]

12 Chapter 1 What Is Jabber Technology?

load level your components across a set of servers to create a naturally scalable set ofservices.

���������

���������

���� ��

�������

���������

���������

���������

�����

Figure 1.1 Jabber Architecture.

These socket-based services can be either the initiator of the connection (that is, theserver waits for them to connect) or the target of the connection (that is, the serverattempts to connect to them when it starts).This provides additional flexibility in thatthe server and its component services don’t necessarily have to be started all together.

TipTo route streams of messages accurately between the various types of componentry, the server and connect-

ed components embed something called an XML namespace into the message. For example, the initializa-

tion sequence coming from a component that is connecting via a TCP socket will have a phrase string in the

message that says jabber:component:accept. The startup of an ordinary client will contain the

phrase jabber:client. In this way, the server can do bookkeeping to sort out where in its internal

tables to begin looking for the appropriate handler libraries.

STDIO

In case you run across older server implementations, we should mention that olderimplementations allowed you to operate via STDIO, starting the service plugin from an

03 0672325365 CH01 1/21/05 2:22 PM Page 12

Page 30: Jabber Developer’s Handbook [Sams 2004]

13Jabber Is…

exec call when the server initialized.This was a fairly tight coupling and fraught withpoints of failure, and in practice was difficult to implement and manage.

It worked such that after the server started the service process, the pair would thencommunicate by using the process’s standard input and standard output streams.This wasa fragile connection path, and was subject to differences in the way different operatingsystems worked. If you run across implementations that use STDIO, you should reimple-ment them specifying jabber:component:accept in the jabber.xml configuation file.(See Chapter 5,“Extending the Jabber Server,” for server details and an implementationexample.)

Although older texts may refer to using STDIO, support for it has been dropped fromthe open source server. Further, it will not be included in future versions of the server.Our understanding is that the commercial “industrial strength” server from Jabber, Inc.will drop support for it as well. Using TCP/IP sockets is a much cleaner and morerobust solution.

Service DiscoveryHaving all these services is great, but you need some way to find them. Jabber provides amechanism for discovering services through browsing that uses XML namespaces toidentify the capabilities of each service.

These capabilities are advertised in the <browse/> element of the jabber.xml config-uration file. Each service has a unique (within this server) Jabber identifier, known as aJID. Here’s an example that we’ll be using later in the book:

<service jid=”weathercheck” name=”Weather Checker”>

<ns>jabber:iq:register</ns>

<ns>jabber:iq:gateway</ns>

</service>

This stanza specifies an internal name (JID) for the service (“weathercheck”) and anexternal name (“Weather Checker”) that gets displayed when a user asks for a display ofservices available for registration, as shown in Figure 1.2:

Figure 1.2 Server information.

03 0672325365 CH01 1/21/05 2:22 PM Page 13

Page 31: Jabber Developer’s Handbook [Sams 2004]

14 Chapter 1 What Is Jabber Technology?

The <ns> elements let the clients and server know to what namespaces the service willrespond.

Chapters 4 and 5 cover building service components extensively, and Chapter 2 cov-ers configuring the server to load components.

Server and Client Interactions At this point, it may prove instructive to see how this potpourri of components all fitstogether. Look, for example, at how personal presence issues are handled between aclient and the server. Suppose a client, dana, wants to change his presence information toDo Not Disturb. He changes his presences in the client GUI, and a message is sent tothe server.

The client constructs and sends an XML fragment (called a “packet” in Jabber-speak)like this to the server:

<presence><show>dnd</show><priority>0</priority></presence>

The server recognizes the tag and chooses the presence module (part of the JabberSession Manager component) to handle the packet, including telling other clients. If youstart up the Jabber server in debug mode (see Chapter 2 for details), you can see thepacket from the client being handled:

First, the server’s managed I/O (MIO) input handler receives the packet from anopen socket between it and the client:

mio.c:760 MIO read from socket 16:

<presence><show>dnd</show><priority>0</priority></presence>

The server sends the presence indication back to this client first and then everyoneon this client’s roster. First, it delivers to the client making the presence change (lines 2-7):

1: mod_presence new presence from dana@localhost/Exodus of

➥ <presence from=’dana@localhost/Exodus’>

➥ <show>dnd</show><priority>0</priority></presence>

2: deliver.c:257 deliver(to[dana@localhost],

➥ from[dana@localhost/Exodus],type[2],packet[<presence

➥ from=’dana@localhost/Exodus’ to=’dana@localhost’>

➥ <show>dnd</show><priority>0</priority></presence>])

3: users.c:143 js_user(dana@localhost,A0AD7B8)

4: deliver.c:55 delivering locally to dana@localhost

5: modules.c:135 mapi_call 3

6: modules.c:158 MAPI A05CEF8

7: mod_presence deliver phase

Next, it delivers to others on the client’s roster. Here, the server attempts to deliver tobill, another client:

8: deliver.c:257 deliver(to[bill@localhost],from[dana@localhost/Exodus],type[2],

➥ packet[<presence from=’dana@localhost/Exodus’ to=’bill@localhost’>

➥ <show>dnd</show><priority>0</priority></presence>])

03 0672325365 CH01 1/21/05 2:22 PM Page 14

Page 32: Jabber Developer’s Handbook [Sams 2004]

15Jabber Is…

9: users.c:143 js_user(bill@localhost,A0AD7B8)

10: deliver.c:55 delivering locally to bill@localhost

11: mod_presence deliver phase

The client, bill, is offline, but because the jabberd doesn’t maintain the state for indi-vidual rosters, it doesn’t know this and the attempt will fail, but it’s a graceful fail fromthe server’s standpoint: It has made a best effort attempt to deliver the message on thefollowing line 13.The packet just gets dropped (line 14). Other packets have differentpolicies for how to handle offline users—if this had been a message rather than a pres-ence packet, it would have been stored and delivered to bill@localhost when he nextlogged in.

12: sessions.c:301 THREAD:SESSION:TO received data from dana@localhost/Exodus!

13: offline.c:54 THREAD:OFFLINE received bill@localhost’s packet:

<presence from=’dana@localhost/Exodus’ to=’bill@localhost’>

<show>dnd</show><priority>0</priority></presence>

14: util.c:75 dropping 503 packet <presence from=’dana@localhost/Exodus’

➥ to=’bill@localhost’>

<show>dnd</show><priority>0</priority></presence>

When bill comes back online, as shown in Figure 1.3, his client wants to informother clients of his return to the land of the living.

His client gets its own roster stored by the server in an XML file (line 1) and the coremodule deliver.c routes the packet containing the roster names (line 3).

1: xdb_file.c:109 loading ./spool/localhost/bill.xml

2: deliver.c:474 DELIVER 1:sessions

<xdb type=’result’ to=’sessions’ from=’bill@localhost’

➥ ns=’jabber:iq:roster’ id=’61’>

<query xmlns=’jabber:iq:roster’>

<item jid=’dana@localhost’ name=’dana’ subscription=’both’>

<group>JabberBook</group>

</item>

<item jid=’jane@localhost’ name=’jane’ subscription=’both’>

<group>JabberBook</group>

</item>

<item jid=’caitlin@localhost’ name=’caitlin’ subscription=’both’>

<group>JabberBook</group>

</item>

</query>

</xdb>

3: deliver.c:678 delivering to instance ‘sessions’

The presence delivery mechanism formulates presence messages for each name in theroster, and determines their presences via their responses or lack thereof. If the packet isnot routable, then the assumption is that the roster member is offline, and bill is updat-ed with the presence responses from his roster members, or an offline indication if theynever respond to his attempt to update their knowledge of his presence. In Figure 1.3,

03 0672325365 CH01 1/21/05 2:22 PM Page 15

Page 33: Jabber Developer’s Handbook [Sams 2004]

16 Chapter 1 What Is Jabber Technology?

the client dana has its debug window open and you can see bill’s presence messagebeing received.

Exodus Tools Help

Available

++++ +

Friends

bill

Exodus Tools Help

Available

++++ +

JabberBook

caitlin

dana

jane

+

JabberBook

bill

dana

StatusAdd Find Rooms

caitlin@localhost [Available]

File Edit View Tools Help

Jabberd

Current JID: dana@localhost/Exodus

RECV: <presence from=‘bill@localhost/Exodus’ to=‘dana@localhost‘><status>available</status>

<priority>0</priority></presence>

RECV: <presence from=‘bill@localhost/Exodus’ to=‘dana@localhost’><status>available</status>

<priority>0</priority><x xmlns=‘jabber:x:delay’from=‘bill@localhost/Exodus’

stamp=‘20021208T16:05:31’/></presence>

Exodus Tools Help

Available

++++ +

X-Debug

JabberBook

bill

caitlin

jabber Powered

Exodus - bill@loc ... X-

Exodus - jane@lo ... X- Jabber Instant Me... X- Exodus - dana@I ... X-

Figure 1.3 Handling presence.

03 0672325365 CH01 1/21/05 2:22 PM Page 16

Page 34: Jabber Developer’s Handbook [Sams 2004]

17Jabber Is…

Presenting this little example here—somewhat out of context, as it were—provides aplace to talk about how one should view writing components. First, it’s important toacknowledge that certain services may be accomplished best as a part of the core Jabberserver—both for speed and efficiency (rosters can get big and the potential for trafficspikes is always there) and because the Jabber server is much like an internet router orswitch: Operations that involve switch-like or hub-like services should be a part of theswitch. Sometimes you may have to bite the bullet, hunker down, and write C code andbundle it directly into the Jabber server.

In the vast majority of cases, however, you are going to want the flexibility to distrib-ute services onto their own CPU (like our national weather service).To achieve servicemanageability, for example, you might want to fix a bug in a service without taking theentire Jabber daemon down.Terminating the weather service with a control-C simplycloses the socket connection to the jabber server.The listen() posted by the server isstill intact.You can fix your bug and restart the distributed component and connect backup to the jabber server[5].

Of course, components are not the only way for Jabber to provide services, whichprovides a nice segue to the next topic.

…Client-based services…The usual, mundane experience of instant messaging for most people involves operatingthrough a client interface with a roster full of other humans. But think about it for amoment: Nothing requires a member of a user’s roster to be a human, right? As long as aJabber client responds to well-formed XML messages from other Jabber clients withwell-formed XML messages, the client is perceived by the Jabber server as a perfectlyacceptable member of a Jabber community.And that gives us a really great mechanismfor delivering services to Jabber-using humans or other applications. Imagine having aclient on your roster that asynchronously delivers messages whenever a change in theweather forecast occurs.Actually, you don’t have to imagine it—extending Jabber toarchitect extended clients is one of the topics covered in Chapter 5.

Figure 1.4 shows two GUI-based clients with humans at the helm.Also shown isanother client (a weather service) which for all intents is always on, and always availableto other clients who add it to their rosters. It has additional logic such that when theinformation provided to it by an Internet source changes, it generates an ordinary instantmessage to all the subscribers on its roster.

As shown, the service has a functional wrapper that encapsulates the capabilities of theweather service, which may be arbitrarily complex (for example, it might be talking tothe Internet or a database).With the simple addition of a Jabber binder, the service canthen appear as a client to other Jabber clients, and thus be added to the roster of human

5 The same thing doesn’t hold true for a component connecting through STDIO (which, as wepointed out previously, is a strongly deprecated usage and eliminated in future server implementa-tions) or one to which the server connects when it starts.

03 0672325365 CH01 1/21/05 2:22 PM Page 17

Page 35: Jabber Developer’s Handbook [Sams 2004]

18 Chapter 1 What Is Jabber Technology?

users.Thus, whenever there’s a change in the weather forecast, the service can notifyhuman users either via presence messages or normal instant messages.The developer ofthe weather service doesn’t have to develop a customer UI for the service—clients expe-rience the service as a conversation with another client.

�������

����

������ ��� ����

�������������� ����������

���� ��� ���� ����� ����

����

����� ����� ����

��������

���� �

���� �

���

����

���

��� !���"�

��� ������ ����� �� ��

���

������ �� ������

��� ������

�� ��� ! "" �#�$% "& �

#����� ������������"���

�' ��� � ���(� � ���

Figure 1.4 Extending the concept of client applications.

Finally, as shown in Figure 1.4, a client can also simply be a script that interacts withthe Jabber server and disconnects.A couple of those are demonstrated in this chapter justto help you whet your appetite and to show how easy it is to write something usefulagainst the Jabber messaging framework.

Note that we will use a couple different terms to talk about client-based services inthis book. One is client and the other is component. Generally, a client is easier to construct(you can find examples in Chapters 3 and 5, for example) when you do not have

03 0672325365 CH01 1/21/05 2:22 PM Page 18

Page 36: Jabber Developer’s Handbook [Sams 2004]

19Jabber Is…

administrative rights to the Java server, but can be less efficient (when lots of traffic isinvolved) than a component, which connects directly to a Jabber server.

The tipping point between implementing something as a client or as a componentcan be delicate. Because a client connects to a server component (in an arrangementcalled c2s) that handles client-to-server message traffic and then connects to the server, itis not as efficient at handling large traffic volumes.Therefore, anything that affectsthroughput on the c2s component connecting a client to the server (such as a single usertransferring a large MP3 file) affects the responsiveness experienced by every end-userrouting over that c2s component.

Another way to look at it is this. Suppose a human user (User A) wants to invoke aclient-based service (let’s call it a Bot for ease).A message going to the Bot goes throughthis path: User A -> c2s component -> server -> c2s component -> Bot -> c2s com-ponent -> server -> c2s component -> User A (4 hops through c2s for the round trip).In contrast, a message going to a component takes this path: User A -> c2s -> server ->Bot -> server -> c2s -> User (2 hops round trip through the c2s component).A per-sonal Bot that will not be heavily burdened is fine as a client, or when you don’t haveadmin rites to a server to set up a component. But the load should always be consideredwhen thinking about which one to write.

We cover both ways of writing client-based services, but we wanted to give you someearly exposure to the idea that there can be more than one way to create services, andno one way is necessarily the “right” way.A personal Bot that keeps your to-do list andthat is accessed by only you is fine as a client, but if you create a groupware picture-shar-ing application, then message load should weigh more heavily in your design decisions.

…Open…Whereas most of the popular IM protocols, clients, and servers—most notably AOLInstant Messenger (AIM)—are closed and proprietary and therefore difficult (though notimpossible) to figure out how to interoperate with or leverage, the Jabber protocol isfree, open, public, and well documented. Unlike certain other open licensing schemes,there is nothing restrictive about using Jabber.You can make derivative software fromit—anything from your own servers to myriad types of clients or components, andrelease it under any licensing scheme you wish.

In our research work, we developed a massive test automation suite using Jabber pro-tocols as the message transport system and Jabber clients as the user interface.We certain-ly could not have done this with the closed and proprietary IM systems.What’s mostimportant for you as developers to seize upon is the fact that lots of good examples ofprojects that use Jabber exist in a number of languages and user experience models.

…Asynchronous…A Jabber server–mediated connection guarantees delivery of messages while the destina-tion client is in session.When a client goes offline, the Jabber server uses store and for-ward to deliver messages to a client when it reconnects.

03 0672325365 CH01 1/21/05 2:22 PM Page 19

Page 37: Jabber Developer’s Handbook [Sams 2004]

20 Chapter 1 What Is Jabber Technology?

The utility of asynchronous guaranteed messaging and store and forward delivery isnot just useful in the realm of messages between humans, by the way.We promote Jabberas a system architecture that enables systems to talk with systems.

Considered as a command and control architecture, this means that even though auser client might go offline, guaranteed, in-order delivery of results is assured. It alsomeans that even though a service that is implemented as a client might go offline, com-mands for the service will be delivered in order when the service returns.You might, forexample, take a service offline to deliver the newest and greatest version, and when itcame back online, it would get all the user input messages sent to it.

TipOf course, a service developer (that is, you) is responsible for maintaining compatibility between versions of

a command-and-control–based client acting as a service. If your previous version accepted update

weather forecast from your clients as input constituting a command, then your current version

should do this, too.

…Extensible…Jabber is extensible in multiple senses of the word. In one sense, Jabber is extensiblebecause you can plug in new capabilities very easily, as we will demonstrate.Additionally,you can use XML namespaces to define new message types.

An XML namespace is a qualifier for XML messages that allows an unambiguousmeaning to the message.The basic idea of namespaces is that the string name in one con-text does not have the same meaning as in another.You can design an application whosemessages are special to that application and specify a namespace that uniquely defines adialog for both participants (client and server) in the application.A typical Jabber serveras shipped does much of its work with only three XML tags: <message/>, <presence/>,and <iq/>. Of these, most of the client-management transactions and transactionresponses are carried in <iq/> tags.With so few defined tags, namespaces are absolutelyessential to give richness to the large number of potential dialogues between servers andclients.

Notice in the earlier example of a client retrieving its roster and then notifying rostermembers that the XML message constituting the roster fetch used a query involving aspecific namespace (xmlns=”jabber:iq:roster).This in effect notified the server thatthis was not just any old query, but a query about rosters.

1: <iq id=”roster_1” type=”get”>

<query xmlns=”jabber:iq:roster”/>

</iq>

And then, when the server delivered the roster to the client, it constructed an XMLmessage like this:

2: <iq id=”roster_1” type=”result”>

<query xmlns=”jabber:iq:roster>

03 0672325365 CH01 1/21/05 2:22 PM Page 20

Page 38: Jabber Developer’s Handbook [Sams 2004]

21Jabber Is…

<item

jid=”[email protected]

name=”Jean Luc”

subscription=”both”/>

<item

jid=”[email protected]

name=”Mr Data”

subscription=”both”>

<group>Friends</group>

</item>

<item

jid=”[email protected]

name=”Number One”

subscription=”both”>

<group>Friends</group>

</item>

</query>

</iq>

From reading the xmlns attribute of the <query> tag, the client knew that the <item>list streamed to it was in fact a roster.

…Decentralized…Earlier, we talked about Jabber as a P2P enabler.We also said that Jabber is set up to beclient-to-server-to-client.This means that all message traffic going from Alice’s computerto Bob’s computer must transit a Jabber server (or a network of peered Jabber servers).Thus, some purists would say that Jabber is not purely P2P.We maintain that there’s verylittle substantive difference from the user’s standpoint and positive differences from thedeveloper’s standpoint. From the client’s standpoint, the alternate address space offered byJabber is perceived as being totally decentralized[6].

A user can connect from any machine on the Internet and converse with a human, asoftware agent, or a service anywhere else on the Internet.

Using a server-based implementation means that you, the developer, can stage anddevelop your code on something as simple as a laptop before deployment, and thenmove your application to a large-scale server or server farm, where user accounts can besecured, backed up, and scaled up as needed.

Servers do maintain direct server-to-server connections to transfer messages fromusers and services hosted on one server to users and services on another.

6 It is possible for Jabber clients to connect directly from socket to socket with one another.Twoend points can negotiate connections, for example to transfer files, but those “out-of-band” con-nections are always negotiated first through the server. In the case where one client is behind afirewall—or both of them are—this may not always be possible, in which case the server will inter-mediate the transfer.

03 0672325365 CH01 1/21/05 2:22 PM Page 21

Page 39: Jabber Developer’s Handbook [Sams 2004]

22 Chapter 1 What Is Jabber Technology?

A Jabber client always connects to a specific Jabber server on a TCP socket over port5222, unless you specify another port, which you might do for a private application, forexample.You can start the server up on another port by modifying a tag in the Jabberserver configuration file, jabber.xml.The bi-directional socket connection allows asyn-chronous operations.Therefore, both clients and server code have to make sure that theydo any necessary cleanup.

…Secure…Even though Jabber clients reach through non-routable intranets, a Jabber server caninsulate itself from the Internet and other Jabber servers by controlling which connec-tions it will accept. In terms of message security, it is possible to use Secure Socket Layer(SSL) to create a secure client-server communication link.

…XML Protocol…Jabber uses an XML-based message set to send queries and ordinary conversationbetween clients. Creating a useful transport and session layer atop XML is really Jabber’sraison d’etre.The Jabber server, at its heart, is really a smart data switch that manages theconnectivity of the applications that are using it as a backplane. Jabber has some otherwonderful capabilities that enrich and inform applications, such as presence and availabil-ity detection and reporting, but fundamentally it’s superb middleware that uses XML toknit distributed applications, the most famous of which are applications that help peopleto type text messages to one another. However, the association of chat applications withJabber is only an artifact of historical primacy.

Riding on TCP/IPJabber uses straight sockets rather than, say, HTTP for a variety of reasons. For one thing,it’s “closer to the bare metal” than HTTP. It doesn’t require that addresses look likeURLs as HTTP does.Also, it can be easier to safely tunnel through firewalls withstraight TCP/IP. Finally, the dynamics of a bi-directional communications can be handledbetter with persistent TCP connections than with stateless HTTP connections.Persistence means that stateful knowledge of the conversation can be maintained for thefull course of the interchange.Therefore, there’s no need to embed cookies or createhidden fields in constructed HTML pages.

Provides Real-time Exchange of MessagesThe preceding section mentioned that the innovators behind Jabber didn’t want JabberIDs to look like URLs.And they don’t—they look like email addresses.The reason forthis is pretty simple when you think about it.

Architecturally, a network of Jabber servers, each serving as (in effect) an ISP for a setof clients, is just like email. Clients receive their various messages from the server towhich they are directly connected. Servers speak to one another to transfer messages forthe clients they host.

03 0672325365 CH01 1/21/05 2:22 PM Page 22

Page 40: Jabber Developer’s Handbook [Sams 2004]

23Jabber Is…

The big difference between email and Jabber instant messaging is that because theserver can tell when a client is online, it delivers the message immediately; otherwise itwill store the message for an offline client and forward it when the client connects tothe server again, acting essentially like email.The potential immediacy of message deliv-ery means that Jabber acts like a real-time messaging backplane (based on sockets)between elements of a distributed application.The store and forward capability meansthat messages destined for an application are guaranteed delivery. Obviously, you as theapplication architect have to contemplate how to handle potentially stale messages.

…And Presence InformationThe idea that presence and availability can be used to make connections to services lessfragile is a really compelling concept. Consider some of the middleware connectionstrategies that have characterized distributed applications in the past, such as CORBA orRMI. Connections across these backplanes always had to have a lot of scaffolding toassure that an application was robust in the face of network failure. If you look atCORBA, RMI, or the standard transports for XML-RPC, SOAP, and other forms ofXML messaging or at tunneling via HTTP, none of these mechanisms provides anyinformation about the availability of nodes in the network.

Jabber addresses the need for a set of open protocols that enable the exchange ofhighly structured information in an asynchronous, real-enough-time manner betweennetwork endpoints.Asynchronous here means that any participant in the conversationcan say anything (okay, not anything, but anything within the definition of the name-space) at any time and other participants are expected to handle the conversation.

When a service publishes a presence message like this:

<presence

from=’[email protected]/20715’

to=’[email protected]/Services’>

<status>Online</status>

</presence>

The meaning is pretty unambiguous.Using Jabber as a backplane, the sides of the application “know” that they can con-

verse, and it’s a given that even if there are transient network failures, when connectionsare re-established the pent-up message stream will be delivered in order.With presenceand availability management, consider how easy it would be for a system administrator toassess the state of a multitude of hosts that are being controlled by having a Jabber clienton each host publish out the state of the host as presence information.

…Between Two Endpoints on the Open InternetBecause Jabber uses a familiar and friendly user@host notation, no special addressingschemes need to be supported.Anything that conforms to normal Internet addressingcan participate, whether it’s a human-piloted chat client or a garage door opener on yourhome network.What’s not good about the addressing scheme is that it relies on the

03 0672325365 CH01 1/21/05 2:22 PM Page 23

Page 41: Jabber Developer’s Handbook [Sams 2004]

24 Chapter 1 What Is Jabber Technology?

normal Internet architecture of DNS to locate host to route to user.This means thatJabber is subject to the restrictions of “normal” Internet addressing and does not use aprivate address space based, for example, on nicknames, as AOL Instant Messenger andYahoo Pager do. On the flip side, because Jabber client addresses are so close to the basicSMTP (Simple Mail Transfer Protocol) scheme, there is no need for a centralized namingauthority, so Jabber is more easily scalable.

…And a Corporate IntranetJabber was meant to run on the open Internet, and therefore it does not “punchthrough” to private non-routable addresses (10.*.*.* or 192.168.*.* networks, for exam-ple) the way that some P2P applications can.This can make it more acceptable to corpo-rate IT mavens, while allowing it to act as a secure network protocol for doing suchthings as network administration.The “Run your own server” model is especially appeal-ing to corporations. In the same way that they have run their own email servers (and forpretty much the same reasons,) they can now run their own internal IM servers.

A Useful Application to Jump-Start Your Interest Finally, some code for us to play with.To illustrate some of these principles, let’s look at acouple implementations of a useful client.

If you have downloaded the jabberd server software and installed it as we detail inChapter 2, you now have a working Jabber server on localhost, your own PC. If youhaven’t done that yet, go ahead and take the time to do it; otherwise you’re missing outon all the fun.We encourage you to do that to follow along with the example. Goahead—Chapter 1 will be here when you come back.

With the jabberd running, we hope you have also tried “talking to yourself ” bybringing up a pair of clients, registering new users through a client,[7] and passing mes-sages between them.

If you haven’t tried ping-ponging messages back and forth, start your jabberd (forexample, in a command window on Windows) and let it run.Then start a session in aclient and open the debug window to look at the stream of XML messages going backand forth.

If you’ve gotten this far, you are exercising Jabber’s P2P capabilities to exchange mes-sages and presence through nice GUI clients, probably using a jabberd server set up onlocalhost.That’s great, and a good place to start with all your experiments with Jabber.You’ll want to try out new things on your localhost server before unleashing them on anunsuspecting world, and just about any machine you would care to write code on isquite capable of hosting a jabberd.

7 A client we currently like for MS Windows is the nicely turned out WinJab client and its newerversion, Exodus.

03 0672325365 CH01 1/21/05 2:22 PM Page 24

Page 42: Jabber Developer’s Handbook [Sams 2004]

25A Useful Application to Jump-Start Your Interest

A Practical User Creation Script (Java version)As we said previously, you don’t have to write complex or even complete applications tointeract with the Jabber framework. Considering Jabber to be a protocol that can absorb,route, and create responses for well-formed XML messages, you can write somethingthat exercises a narrow functionality within the larger space of the server.Writing “justenough” jabber is something we will show many times in this book (possibly leavingadditional elaboration to the reader, as they say).

Here’s a user Java account creation script in its entirety. Let’s look at it this way firstand then break it down into conceptually complete subparts.To operate it from a com-mand line, you will need to install a Java runtime or JDK (see Appendix D for a URLreference) and the JabberBeans library (see Appendix D for a URL reference).

Start by entering the following code into your favorite text editor and saving it asNewUser.java.

Listing 1.1—NewUser.java

1:// example user account creation script

2:import java.net.*;

3:

4:import org.jabber.jabberbeans.*;

5:import org.jabber.jabberbeans.util.JID;

6:import org.jabber.jabberbeans.Extension.*;

7:

8:public class NewUser {

9: private String userName;

10: private String password;

11: private String jabberServerName;

12:

13: public NewUser(String u, String p, String h) {

14: this.jabberServerName = h;

15: this.userName = u;

16: this.password = p;

17: }

18: public boolean register() {

19: // fire up a connection to the host grabbed from the command line and

20: // register the username, password, and a resource name with the

21: // server. If you have succeeded, then you will find a <username>.xml

22: // in the default directory -- usually <installdir>/spool/<hostname>.

23: System.err.println(

24: “user name -->”

25: + userName

26: + “ password-->”

27: + password

28: + “ server-->”

03 0672325365 CH01 1/21/05 2:22 PM Page 25

Page 43: Jabber Developer’s Handbook [Sams 2004]

26 Chapter 1 What Is Jabber Technology?

29: + this.jabberServerName);

30: InfoQueryBuilder iqb;

31: IQRegisterBuilder iqRegb;

32: iqb = new InfoQueryBuilder();

33: iqRegb = new IQRegisterBuilder();

34: iqb.setType(“set”);

35: iqb.setToAddress(new JID(jabberServerName));

36: iqRegb.set(“username”, userName);

37: iqRegb.set(“password”, password);

38: try {

39: iqb.addExtension(iqRegb.build());

40: ConnectionBean cb = new ConnectionBean();

41: cb.connect(InetAddress.getByName(jabberServerName));

42: cb.send((InfoQuery) iqb.build());

43: } catch (Exception e) {

44: e.printStackTrace();

45: return false;

46: }

47: return true;

48: }

49:

50: public static void main(String[] args) {

51: if (args.length != 3) {

52: useage();

53: System.exit(0);

54: }

55: NewUser n = new NewUser(args[0], args[1], args[2]);

56: System.out.println(

57: “Registration was “

58: + (n.register() ? “Successful.” : “Not Successful.”));

59: }

60:

61: public static void useage() {

62: System.err.println(“Usage: NewUser <name> <password> <hostname>”);

63: }

64:}

If you want to use this script yourself, then you should just edit this code into a .javafile with your favorite code editor or IDE, compile it into a .class file.Then tosstogether a little .bat or .sh file to work from.

A Newuser.bat for our Windows platform looks like:

C:\java\bin\java.exe -classpath <jabberbeanshome>\jabberbeans.jar;<projecthome-for-NewUser-javaclassfile> NewUser %1 %2 %3

Listing 1.1—Continued

03 0672325365 CH01 1/21/05 2:22 PM Page 26

Page 44: Jabber Developer’s Handbook [Sams 2004]

27A Useful Application to Jump-Start Your Interest

You will have to adjust the locations in the classpath as appropriate.The basic idea isthat you will need the JabberBeans library from wherever you installed it and the foldercontaining the .class file.

You would expect the script invoker to use a command line like this:

NewUser <name> <password> <hostname>

So what’s happening in this script-like Java program? These lines bring in theJabberBeans, the Java Jabber library:

4:import org.jabber.jabberbeans.*;

5:import org.jabber.jabberbeans.util.JID;

6:import org.jabber.jabberbeans.Extension.*;

And here, we import the package containing InetAddress as a convenience, becausewe’ll be asking for the Internet address of the named Jabber Server a bit farther down inthe code.

2: import java.net.*;

You could have done this all in the main() method and avoided some of the classloading overhead, but it’s barely noticeable and you may want to use this class later as apart of a larger library. So we take the hit and make a NewUser class, then instantiate it inmain().

8:public class NewUser {

9: private String userName;

10: private String password;

11: private String jabberServerName;

12:

13: public NewUser(String u, String p, String h) {

14: this.jabberServerName = h;

15: this.userName = u;

16: this.password = p;

17: }

Okay, next is the meat of this Java-based script; the thing we really want to showcasefor you. Here, we fire up a connection to the host grabbed from the command line andregister the username, password, and a resource name with the server. If you have suc-ceeded, then you will find a <username>.xml in the default directory, usually<installdir>/spool/<hostname>.

In public boolean register(), you first give yourself a blank slate for creating an<iq> message (InfoQueryBuilder iqb), and then fill it up with registration-specific ele-ments (IQRegisterBuilder iqRegb).The InfoQuery built by the InfoQueryBuilderacts as an overall container for the registration message built by the IQRegisterBuilder.A generic InfoQuery looks like this:

<iq type=”get|set|result|error”>

<query xmlns=”........”>

03 0672325365 CH01 1/21/05 2:22 PM Page 27

Page 45: Jabber Developer’s Handbook [Sams 2004]

28 Chapter 1 What Is Jabber Technology?

information custom to the namespace..

</query>

<iq>

If you invoke the script like this on Windows:

NewUser “alpha” “budgie” “localhost”

The packet that you should build in this specific case looks like this:

<iq to=”localhost” id=”sn-0” type=”set”>

<query xmlns=”jabber:iq:register”>

<username>alpha</username>

<password>budgie</password>

</query>

</iq>

Notice that you are giving the <iq> a <query> by using the namespace xmlns=”jabber:iq:register” and you are filling in information that is relevant to that name-space.The Jabber server uses the namespace to decode what you are trying to say to it.

An InfoQueryBuilder is an absolutely essential abstraction because the number ofpossible fields in an InfoQuery object is far too great. Constructing an InfoQuery mes-sage from scratch is something you wouldn’t wish on the worst developer in your cubi-cle farm.

TipIn this case, we are building an InfoQuery, but there is also a composite class for messages

(MessageBuilder) and presence messages (PresenceBuilder).

At any rate, you first create instances of the builder classes, which are initialized withempty values.

18: public boolean register() {

30: InfoQueryBuilder iqb;

31: IQRegisterBuilder iqRegb;

32: iqb = new InfoQueryBuilder();

33: iqRegb = new IQRegisterBuilder();

Next, you give the query a well-formed type, and specify a recipient (the Jabber serv-er acting as the ISP). Notice that there is a general set() method to set an arbitraryname-value pair. In this case you want to provide a possible user ID and password pair.You could have set a few more parameters, of course. One way to find out what a par-ticular Jabber server instance would like to see would be to ask the server first by send-ing it an InfoQuery packet.We’ll show you a technique for doing that in the Pythonexample a little further along in this section.

03 0672325365 CH01 1/21/05 2:22 PM Page 28

Page 46: Jabber Developer’s Handbook [Sams 2004]

29A Useful Application to Jump-Start Your Interest

You set the query type and the recipient for the outer container of the message asfollows:

34: iqb.setType(“set”);

35: iqb.setToAddress(new JID(jabberServerName));

Next, you set the two relevant name-value pairs of the message’s query-specific con-tent (line 36-37); then you add that content to the query’s message container (line 39).

36: iqRegb.set(“username”, userName);

37: iqRegb.set(“password”, password);

38: try {

39: iqb.addExtension(iqRegb.build());

Finally, you create a ConnectionBean to deliver the packet to the server, connect thebean up to the server socket (line 40), and send the packet (line 42).

40: ConnectionBean cb = new ConnectionBean();

41: cb.connect(InetAddress.getByName(jabberServerName));

42: cb.send((InfoQuery) iqb.build());

The server will return a packet basically acknowledging the query, but in our hubris,we’ll assume that the attempt is successful.To make this example a little more bullet-proof, we should add listeners to catch and examine the return from the server, but we’llleave that to you.

If everything worked as we assumed, the server will have created a file calledalpha.xml, which serves as part of the server’s database.The file should closely resemblethe following:

1: <xdb>

2: <query xmlns=”jabber:iq:last” last=”1038790074”

xdbns=”jabber:iq:last”>Registered</query>

3: <password xmlns=”jabber:iq:auth” xdbns=”jabber:iq:auth”>

➥ budgie</password>

4: <query xmlns=”jabber:iq:register” xdbns=”jabber:iq:register”>

5: <username>alpha</username>

6: <password xmlns=”jabber:iq:auth”>budgie</password>

7: <x xmlns=”jabber:x:delay” stamp=”20021202T00:47:54”>registered</x>

8: </query>

9: <foo xdbns=”jabber:x:offline” xmlns=”jabber:x:offline”>

10: <message from=”localhost” to=”alpha@localhost”>

11: <subject>Welcome!</subject>

12: <body>Welcome to the Jabber server at localhost –

we hope you enjoy this service! For information about

➥ how to use Jabber,

visit the Jabber User’s Guide at http://docs.jabber.org/</body>

13: <x xmlns=”jabber:x:delay” from=”alpha@localhost”

➥ stamp=”20021202T00:47:54”>

Offline Storage

</x>

03 0672325365 CH01 1/21/05 2:22 PM Page 29

Page 47: Jabber Developer’s Handbook [Sams 2004]

30 Chapter 1 What Is Jabber Technology?

14: </message>

15: </foo>

16: </xdb>

Notice that in this example we implemented just enough code to get a simple jobdone. No more, no less. Jabber enables you to make small talk (short and purposefulconversations) or complete dialogues.We will follow this principle every time in ourexamples.

NoteOur server is configured to send all new users a welcome message when they are first created. Because the

script disconnects right after making the new user, the welcome message is stored for delivery the next

(actually, first) time the new user logs in. The stored welcome message is lines 10 through 14.

A Practical User Creation Script (Python version)Here’s the same example in Python.To use Jabber from Python, you need to downloadand install Python 2.x and the JabberPy libraries (see Appendix D).An easy way to installJabberPy (after installing Python if you haven’t already done so) is to use setup.pybundled with JabberPy. Untar the downloadable file, and as root on Linux, or an admin-istrative user on Windows, open a command window, cd to the directory containingsetyp.py, and from the command line run the following:

python setup.py install

Alternatively, just copy xmlstream.py and jabber.py to somewhere in yourPYTHONPATH. For example, copy them into <python-install-folder>/site-packages/JabberPy.After you’ve done this one-time operation, you should beready to script Jabber from Python.

To use the script from the command line, use

python newUser.py localhost alpha budgie Work

Import the utility libraries and the Jabber Python libraries.

Listing 1.2—newUser.py

1:#!/usr/bin/env python2

2:import socket

3:from select import select

4:from string import split,strip,join

5:import sys,os

6:from time import sleep

7:import jabber

8:

03 0672325365 CH01 1/21/05 2:22 PM Page 30

Page 48: Jabber Developer’s Handbook [Sams 2004]

31A Useful Application to Jump-Start Your Interest

Create a class that can be called from the command line (lines 81-88), or serve as alibrary.

9:class NewUser:

10:

11: True = 1

12: False = 0

13:

14: def __init__(self):

15: sys.path.insert(1, os.path.join(sys.path[0], ‘..’))

16: if len(sys.argv) == 1:

17: self.usage()

18: sys.exit(o)

19:

In registerNewUser, you need to construct a connection instance using the Clientclass on line 26 and then connect to the server. It’s a matter of style to do it this way.Alternatively, you could have set parameters and then connected as we did in the Javaexample.

20: def registerNewUser(self):

21: print “Register a New User”

22: jabberServer = sys.argv[1]

23: userName = ‘’

24: Password = ‘’

25: Resource = ‘default’

26: con = jabber.Client(host=jabberServer, debug=True, log=sys.stderr)

27: try:

28: con.connect()

29: except IOError, e:

30: print “Couldn’t connect: %s” % e

31: sys.exit(0)

32: else:

33: print “Connected”

34:

Now (lines 35-38) you can set event handlers for various messages you might expectfrom the server:

35: con.setMessageHandler(self.messageCB)

36: con.setPresenceHandler(self.presenceCB)

37: con.setIqHandler(self.iqCB)

38: con.setDisconnectHandler(self.disconnectedCB)

39:

40: # Set up the jabber account

41: con.setRegInfo(‘username’, sys.argv[2])

42: con.setRegInfo(‘password’, sys.argv[3])

43: con.setRegInfo(‘resource’, sys.argv[4])

44: con.sendRegInfo()

45:

03 0672325365 CH01 1/21/05 2:22 PM Page 31

Page 49: Jabber Developer’s Handbook [Sams 2004]

32 Chapter 1 What Is Jabber Technology?

You are essentially registered now and can disconnect and exit.

46: con.disconnect()

47: sys.exit(0)

48:#

Define a simple usage generator:

49: def usage(self):

50: print “%s is a python jabber client to register a new user” % sys.argv[0]

51: print “usage:”

52:

53: print “%s <server> <username> <password> <resource>” % sys.argv[0]

54: print “ - connect to server and login “

55: sys.exit(0)

Here are the various callbacks required by the event model in JabberPy.They areessentially empty, but needed for completeness.

56:

57: def messageCB(self, con, msg):

58: “””Called when a message is recieved”””

59: print “INFO: messageCB”

60: def presenceCB(self, con, prs):

61: “””Called when a presence is recieved”””

62: print “INFO: presenceCB”

63: def iqCB(self, con,iq):

64: print “INFO: recieved InfoQuery”

65: print “Iq:”, str(iq)

66: def disconnectedCB(self, con):

67: print “ERROR: network disconect”

68: sys.exit(1)

69:

Finally, the calling method:

70:if __name__ == ‘__main__’:

71: n = NewUser()

72: if len(sys.argv) == 5:

73: n.registerNewUser()

74: sys.exit(0)

75: else:

76: n.usage()

77: sys.exit(0)

03 0672325365 CH01 1/21/05 2:22 PM Page 32

Page 50: Jabber Developer’s Handbook [Sams 2004]

33A Useful Application to Jump-Start Your Interest

Jabber’s Open Source Development Heritage (And ItsImplications)There are many exemplary usages of Jabber: commercial grade servers from Jabber, Inc.(www.jabber.com) that can and do host many thousands of simultaneous users, freeservers from the Jabber Software Foundation (jabber.org), some commercial and many (!)free clients (again see the listings at jabber.org).

When you use Jabber in whatever capacity—as a message transport system, intermix-ing ready-made clients with your own services—nothing in the licensing agreement pre-vents you.You should as a matter of honesty and developer courtesy, give credit whereit’s due, but in general you are free to use Jabber, and leverage the community in pursuitof your own dreams and schemes. Be aware, however, that some third-party packages andmodules are deployed by their authors under more or less restrictive licenses, such that ifyou want to use them commercially, you might not be able to use them at all or youmight have to make separate arrangements with those authors.

Our philosophy persuades us to give back in greater measure than we receive, butyour mileage may vary. Certainly, after amazing your friends and employers with some ofthe nifty things possible with Jabber, you may well want to become more deeplyinvolved with the Jabber community, and that’s our hope too.

In any event, you may choose to write code for profit that you base on Jabber codebased on Jabber services and messaging.The Jabber community does not preclude thatpossibility for those who might want it. Jabber’s license, although a recognized opensource license, accommodates that.

Conclusion (or Rather,The Beginning)We hope that after reading this chapter you cannot wait to begin digging into the rest ofthe book.At this point you should have come away with the idea that this “Developer’sHandbook” is oriented toward providing you with insightful examples and a good fun-damental understanding of the available APIs.

Finally, you should expect a lot of exploration of the nooks and crannies of Jabberand the philosophy of P2P solutions in general.As a result you should also anticipatesample source code that actually works.

We hope—and endeavor—to exceed your expectations.

03 0672325365 CH01 1/21/05 2:22 PM Page 33

Page 51: Jabber Developer’s Handbook [Sams 2004]

03 0672325365 CH01 1/21/05 2:22 PM Page 34

Page 52: Jabber Developer’s Handbook [Sams 2004]

2Installing and Configuring

Jabber Software

Farther along we’ll know all about it / Farther along we’ll understand why / Cheer up my brother, live in the sunshine / We’ll understand it all by and by.

Traditional gospel song

NOW THAT CHAPTER 1 HAS SET the stage by discussing clients and servers and howthey operate in the overall Jabber architecture, the next three chapters explore the serverand the client in greater detail.This chapter describes how to set up Jabber servers andother software so that you can try the examples in this book.

Downloading the Server SoftwareThe first thing you’ll need to do is download the jabberd server software.There are sev-eral Jabber servers, including commercial ones, but we’ll focus here on the open sourcereference implementation managed by the community at jabber.org.

First, download the installation package appropriate for your operating system. ForLinux and other Unixes, the software is available athttp://jabberd.jabberstudio.org/downloads.As of this writing, the current versionof jabberd is 1.4.2, so the installation file is called jabber-1.4.2.tar.gz. It contains thesource code and build files required for the Jabber server. Download this file to yourUnix machine.

You can use the same jabberd source code package under Microsoft Windows just thesame as under Unix if you have the Cygwin Unix tools installed.They are available athttp://www.cygwin.com. If you don’t want to compile the server yourself, you caninstall the pre-built server.A pre-built installation package for Microsoft Windows is

04 0672325365 CH02 1/21/05 2:22 PM Page 35

Page 53: Jabber Developer’s Handbook [Sams 2004]

36 Chapter 2 Installing and Configuring Jabber Software

available at http://jabberd.jabberstudio.org as the file called JabberD-1.4.2.exe.If you’re working under Windows, you may find it a more convenient way to get startedthan building the source.

Installing the Server SoftwareNow that you have the software, let’s look at how to get it up and running. Here theUnix and Windows differ quite a bit, so they’re covered separately. Feel free to skip thepart that doesn’t apply to you.

Linux and UnixThe download package for Unix contains source code that you’ll need to compile foryour operating system and server hardware. Fortunately, this is easy because the distribu-tion contains the scripts necessary to build on most common platforms including Linux,Solaris, FreeBSD, NetBSD, MacOS (Darwin), and AIX.The examples in this section arefrom a RedHat Linux installation, but other Unix variants should be similar.The jabberdserver is written in C, so you need to have the gcc compiler and the GNU make toolsinstalled for this to work.

Unpack the gzipped source code archive file by entering the following:

# gunzip –c jabber-1.4.2.tar.gz | tar xf –

There’s no separate installation step for the jabberd server—you run it right out of itsbuild directory. So decide where you want the software to run from and unpack thesources there.The directory path in which you compile jabberd is actually compiled intothe server and used to search for configuration files, so although it’s not too onerous, it’seasier to not try to move the server to a different directory after it’s compiled.Unpacking the gzipped tar file creates a directory called jabber-1.4.2. Change direc-tory (cd) into that directory and type:

# ./configure

You should see some output that looks like this:

# ./configure

Running Jabber Configure

========================

Getting pth settings..../configure: line 1: pth-config: command not found

./configure: line 1: pth-config: command not found

./configure: line 1: pth-config: command not found

./configure: line 1: pth-config: command not found

Configuring GNU Pth (Portable Threads), Version 1.4.0 (24-Mar-2001)

Copyright (c) 1999-2001 Ralf S. Engelschall <[email protected]>

loading cache ./config.cache

Platform: i686-redhat-linux-gnu2.4glibc2.3

Build Tools:

04 0672325365 CH02 1/21/05 2:22 PM Page 36

Page 54: Jabber Developer’s Handbook [Sams 2004]

37Installing the Server Software

checking for gcc.. (cached) gcc

checking whether the C compiler (gcc ) works.. yes

checking whether the C compiler (gcc ) is a cross-compiler.. no

checking whether we are using GNU C.. (cached) yes

checking whether gcc accepts -g.. (cached) yes

checking how to run the C preprocessor.. (cached) gcc -E

checking whether make sets ${MAKE}.. (cached) yes

checking for compiler option -pipe.. (cached) yes

checking for compilation debug mode.. disabled

checking for compilation profile mode.. disabled

checking for compilation optimization mode.. disabled

Setting Build Parameters.. Done.

Generating Settings Script.. Done.

You may now type ‘make’ to build your new Jabber system.

#

The configure command checks various system settings and capabilities and creates amakefile that can be used to build jabberd.

After configure finishes, type this command to build jabberd:

# make

This compiles all the server code and links it into an executable called jabberd in the./jabberd directory.The output of this command should look something like this:

# make

Making all in pthsock

make[1]: Entering directory ‘/usr/local/jabber-1.4.2/pthsock’

gcc -g -Wall -I. -I. -I/usr/local/jabber-1.4.2/jabberd/pth-1.4.0 -fPIC -I../jabberd/

-c -o client.o client.c

gcc -g -Wall -I. -I. -I/usr/local/jabber-1.4.2/jabberd/pth-1.4.0 -fPIC -I../jabberd/

-shared -o pthsock_client.so client.o -ldl -lresolv

make[1]: Leaving directory ‘/usr/local/jabber-1.4.2/pthsock’

Making all in xdb_file

make[1]: Entering directory ‘/usr/local/jabber-1.4.2/xdb_file’

gcc -g -Wall -I. -I. -I/usr/local/jabber-1.4.2/jabberd/pth-1.4.0 -fPIC -I../jabberd

-c -o xdb_file.o xdb_file.c

gcc -g -Wall -I. -I. -I/usr/local/jabber-1.4.2/jabberd/pth-1.4.0 -fPIC -I../jabberd

-shared -o xdb_file.so xdb_file.o -ldl -lresolv

make[1]: Leaving directory ‘/usr/local/jabber-1.4.2/xdb_file’

Making all in dnsrv

make[1]: Entering directory ‘/usr/local/jabber-1.4.2/dnsrv’

gcc -g -Wall -I. -I. -I/usr/local/jabber-1.4.2/jabberd/pth-1.4.0 -fPIC -I../jabberd/

-c -o dnsrv.o dnsrv.c

04 0672325365 CH02 1/21/05 2:22 PM Page 37

Page 55: Jabber Developer’s Handbook [Sams 2004]

38 Chapter 2 Installing and Configuring Jabber Software

gcc -g -Wall -I. -I. -I/usr/local/jabber-1.4.2/jabberd/pth-1.4.0 -fPIC -I../jabberd/

-c -o srv_resolv.o srv_resolv.c

gcc -g -Wall -I. -I. -I/usr/local/jabber-1.4.2/jabberd/pth-1.4.0 -fPIC -I../jabberd/

-shared -o dnsrv.so dnsrv.o srv_resolv.o

make[1]: Leaving directory ‘/usr/local/jabber-1.4.2/dnsrv’

Making all in jsm

make[1]: Entering directory ‘/usr/local/jabber-1.4.2/jsm’

Making all in modules

make[2]: Entering directory ‘/usr/local/jabber-1.4.2/jsm/modules’

gcc -g -Wall -I. -I. -I/usr/local/jabber-1.4.2/jabberd/pth-1.4.0 -fPIC -I../../jabberd/

-c -o mod_admin.o mod_admin.c

gcc -g -Wall -I. -I. -I/usr/local/jabber-1.4.2/jabberd/pth-1.4.0 -fPIC

-DHOME=”\”/usr/local/jabber-1.4.2\”” -DCONFIGXML=”\”jabber.xml\””

-c -o static.o static.c

gcc -g -Wall -I. -I. -I/usr/local/jabber-1.4.2/jabberd/pth-1.4.0 -fPIC

-DHOME=”\”/usr/local/jabber-1.4.2\”” -DCONFIGXML=”\”jabber.xml\””

-c -o log.o log.c

gcc -g -Wall -I. -I. -I/usr/local/jabber-1.4.2/jabberd/pth-1.4.0 -fPIC

-DHOME=”\”/usr/local/jabber-1.4.2\”” -DCONFIGXML=”\”jabber.xml\””

-o jabberd config.o mio.o mio_raw.o mio_xml.o mio_ssl.o deliver.o

heartbeat.o jabberd.o load.o xdb.o mtq.o static.o log.o lib/expat.o

lib/genhash.o lib/hashtable.o lib/jid.o lib/jpacket.o lib/jutil.o lib/

karma.o lib/pool.o lib/pproxy.o lib/rate.o lib/sha.o lib/snprintf.o lib/

socket.o lib/str.o lib/xmlnode.o lib/xmlparse.o lib/xmlrole.o lib/

xmltok.o lib/xstream.o lib/xhash.o base/base_connect.o base/

base_dynamic.o base/base_exec.o base/base_stdout.o base/

base_accept.o base/base_file.o base/base_format.o base/

base_stderr.o base/base_to.o -Wl,--export-dynamic -ldl -lresolv

/usr/local/jabber-1.4.2/jabberd/pth-1.4.0/pth_*.o

make[2]: Leaving directory ‘/usr/local/jabber-1.4.2/jabberd’

make[1]: Leaving directory ‘/usr/local/jabber-1.4.2/jabberd’

make[1]: Entering directory ‘/usr/local/jabber-1.4.2’

make[1]: Nothing to be done for ‘all-local’.

make[1]: Leaving directory ‘/usr/local/jabber-1.4.2’

#

NoteDuring the compilation, you may see a message that says:

Now please type ‘make test’ to run a quick test suite. Hope it works.

The distribution includes the GNU Portable Threads library, which includes a test suite. The make test is

referring to a test of the threads library, not jabberd, so there is no actual need to type make test.

04 0672325365 CH02 1/21/05 2:22 PM Page 38

Page 56: Jabber Developer’s Handbook [Sams 2004]

39Installing the Server Software

As we mentioned, there is no installation or make install step. If jabberd compileswithout errors, you can just run it by typing ./jabberd/jabberd, but you’ll want toedit the jabber.xml file first.The following sections cover this.

WindowsIf you’re going to build the source code under Windows, just start a Cygwin bash shelland follow the Unix instructions already explained.To use the pre-built installer, just runit and select the options you want.As shown in Figure 2.1, you have to select JabberD toinstall the server itself. Select Start Menu Shortcuts to install jabberd entries in theWindows Start menu. Select Startup Menu Entry to have jabberd start every time youlog in to Windows.

Figure 2.1 Selecting the installation options under windows.

Click Next and select an installation directory as shown in Figure 2.2. Click Install andthat’s it. Before you run the server, though, you should edit the jabber.xml file.That’snext.

Figure 2.2 Selecting the installation directory under Windows.

04 0672325365 CH02 1/21/05 2:22 PM Page 39

Page 57: Jabber Developer’s Handbook [Sams 2004]

40 Chapter 2 Installing and Configuring Jabber Software

Initial Server ConfigurationThe behavior of the Jabber server is controlled by the jabber.xml configuration file andcommand-line arguments.There are many options for each, but most of the defaultswork fine for getting started.

Jabberd Command-line ArgumentsTable 2.1 summarizes the command-line arguments that control Jabberd.

Table 2.1 Jabberd Command-line Options

Option Description

-c <file> Specifies the location of the jabber.xml configuration file.

-i <file,file> Specifies a number of config files to be parsed.

-D Enables debug output and keeps jabberd as the foreground process.

-H Location where jabberd will look for jabber.xml.This defaults to thedirectory in which you compiled jabberd.

-B Causes the jabberd process to detach from the terminal and run in thebackground.This is useful when starting jabberd from a system startupscript.

-Z <zones> Debug zones.This is useful if you want to see debug messages from justcertain parts of the server.

-v (or V) Server version.-U <user> Attempts to run jabberd as the specified user.

The debug “zones” are just basenames of the C source code filenames, such as mio.c andlog.c, that were compiled into jabberd. So if you want to see debugging messages fromjust the mio.c and log.c files, you’d start jabberd like this:

# ./jabberd/jabberd –D –Z mio,log

TipYou have to provide the –D option to get any debug logging at all turned on, so using –Z without –D does

not cause any debug output to be generated.

The jabber.xml FileThe jabber.xml file is used to configure which services jabberd will provide and howthe server relates to clients and other Jabber servers.The default jabber.xml containsseveral sections, which are examined in detail in the remainder of this chapter.

The jabber.xml file is an XML document (see Appendix B for an introduction toXML) and at its top level, each element delimited by the <service> tag directs the serv-er to load a component and provides configuration information for that component.All

04 0672325365 CH02 1/21/05 2:22 PM Page 40

Page 58: Jabber Developer’s Handbook [Sams 2004]

41Initial Server Configuration

the services needed for simple instant messaging are included in the default configura-tion.

Table 2.2 The Default Jabberd Services

Default Jabber Services Internal Service Name

Jabber session manager (JSM) sessions

Client-to-server connections c2s

Server-to-server connections s2s

Host name resolution (DNS) services dnsrv

Database services xdb

Transaction logging rlogger

Error logging elogger

The jabber.xml file that comes with the distribution is set up to provide basic func-tionality for a single isolated server. It assumes that your server hostname is localhost andstarts the c2s and s2s services on their default TCP/IP ports.Although this enablesclients to connect to the server if they’re running on the same computer as the server,there are some basic configuration options that you should probably set before firing upthe server.

The Jabber session manager (JSM) service controls basic server information and clientregistration information. For example, this line (near the top, in the sessions servicesection) defines the name of the Jabber server:

<host><jabberd:cmdline flag=”h”>my-hostname</jabberd:cmdline></host>

Note the special tag <jabberd:cmdline>. Using this tag enables you to override thejabber.xml setting with a command-line option as specified by the flag attribute (“h”in this example).With the <host> element configured this way, you could now run jab-berd like this:

# jabberd –h other-hostname

to override the value my-hostname in jabber.xml with other-hostname. Set thisline’s value to match the hostname of the computer that you’ll be using to run the server.

TipYou can specify multiple <host> entries to set up your server to run multiple virtual Jabber servers. So if

you configure your server with <host> entries like this:

<host>jabber.virtucorp.com</host>

<host>jabber.mydomain.org</host>

There will be one Jabber server that answers to the name jabber.virtucorp.com and another that

answers to the name jabber.mydomain.org, both running within the single jabberd instance. See

the c2s section later in this chapter to see how to enable a single Jabber server to respond to multiple

names.

04 0672325365 CH02 1/21/05 2:22 PM Page 41

Page 59: Jabber Developer’s Handbook [Sams 2004]

42 Chapter 2 Installing and Configuring Jabber Software

A little farther down in the file is this line:

<update><jabberd:cmdline flag=”h”>localhost</jabberd:cmdline></update>

It tells the JSM to ask the server at update.jabber.org whether a new version of thesoftware is available when jabberd starts.There’s no reason to leave this enabled and ifyour server is behind a firewall or disconnected from the Internet; it won’t work anyway.You can disable it by deleting the line or commenting it out like this:

<!-- <update><jabberd:cmdline flag=”h”>localhost</jabberd:cmdline></update> -->

If you want to use the update feature, change localhost to the hostname of yourserver computer.

The next item of interest in the jabber.xml file is this line:

<vcard2jud/>

This rather cryptic directive causes the server to try to register new users with theJabber User Directory (discussed under “Common Optional Services” later in this chap-ter). Unless you install your own user directory (also below) this will tell the server to tryto send vCard information to the user registry at users.jabber.org. Probably not whatyou want.You can delete this line or comment it out like this:

<!-- <vcard2jud/> -->

The <browse> section of the sessions (JSM) service comes next. It defines the set ofservices that clients can browse. Like the earlier examples, if you’re isolated from theInternet, you don’t need the Jabber User Directory section, so you can comment it outlike this:

<!--

<service type=”jud” jid=”users.jabber.org” name=”Jabber User Directory”>

<ns>jabber:iq:search</ns>

<ns>jabber:iq:register</ns>

</service>

-->

The next entry that you might need to change to get started is in the client connec-tion service (c2s).The default TCP/IP port for clients to connect to the server is 5222.Some IM clients don’t use any port except 5222, so change this only if you have to.Theconfiguration line looks like this:

<ip port=”5222”/>

This tells the server to listen for client connections on port 5222.The last piece of basic configuration controls the port on which the server listens for

messages from other servers.Although you probably won’t be getting any messages fromother servers right away, it’s good to know that the default port is 5269 and it’s specifiedin the s2s service configuration section like this:

<ip port=”5269”/>

04 0672325365 CH02 1/21/05 2:22 PM Page 42

Page 60: Jabber Developer’s Handbook [Sams 2004]

43Initial Server Configuration

Starting jabberdNow that the jabber.xml file is set, you can start the Jabber server by cding to thedirectory where the server code was unpacked and compiled and typing:

# ./jabberd/jabberd

The server reads the jabber.xml file, loads and configures the services, and is ready tohandle connections.Actually, because the path to the installation directory (the Jabber“home” directory) was compiled into the server or set explicitly with the –H command-line option, you can run jabberd from anywhere and it will find jabber.xml in its homedirectory.

You can stop the server by typing Ctrl+C in the window in which you started jab-berd. If you had it detach from the terminal (run in the background) with the –Bcommand-line option, you can use the file in the Jabber home directory that containsthe process ID for the server (called jabber.pid by default), but you can change it in jabber.xml) and use a command like this to kill it:

# kill `cat jabber.pid`

If something goes wrong and your server doesn’t start up, it’s probably either a syntaxerror in the jabber.xml file, a leftover PID file, or a problem binding to the c2s and/ors2sTCP/IP ports. If you have an error in the jabber.xml file, you’re likely to see amessage like this:

# ./jabberd/jabberd

Configuration parsing using jabber.xml failed: mismatched tag at

➥ line 650 and column 2

#

Sometimes the error message can be a little misleading. It may say that the problem ison line 650, but the error was really somewhere before there.This happens a lot if youopen a tag (say <service>) but forget to close it (with a </service> tag).The parser cancontinue quite a way down the file before it realizes that there’s a problem.A good XMLeditor can help you spot and correct the problem.

Normally, the jabberd deletes the file containing its process ID when it exits. But ifthe server exits abnormally (crashes), it can leave the PID file around and it will refuse tostart if a pidfile already exists.We suppose this is to keep you from accidentally runningtwo instances of the server. If this happens, you’ll see a message like this:

# jabberd/jabberd

A pidfile already exists at the specified location. Check to ensure another

➥ copy of the server is not running, or remove the existing file.

Pretty self-explanatory. If another jabberd is running, kill it. If not, just delete the pid-file (jabber.pid) and try again.

The other common problem is when the server fails to open the TCP/IP ports forlistening.This can happen if some other program (most likely another jabberd) alreadyhas them open. If you see messages like these when the server starts, you have this problem:

04 0672325365 CH02 1/21/05 2:22 PM Page 43

Page 61: Jabber Developer’s Handbook [Sams 2004]

44 Chapter 2 Installing and Configuring Jabber Software

20021102T20:36:37: [alert] (-internal): io_select unable to listen on

➥ 5222 [(null)]

20021102T20:36:37: [alert] (-internal): io_select unable to listen on

➥ 5269 [(null)]

You can use the netstat command to verify whether another process is actually lis-tening on those ports. If not, try listening on a specific address and port instead of thedefault address (which the default jabber.xml uses). Change the c2s and s2s configura-tions so that rather than:

<ip port=”5222”/>

you use something like:

<ip port=”5222”>192.168.1.1</ip>

Substitute your real IP address for 192.168.1.1, of course.

CautionIf you use 127.0.0.1 in the preceding example, you’ll be able to connect to the server only from the same

computer it’s running on.

If you’re just setting up your Jabber server for the first time, that’s all you probably needto know to get it up and running. If you’d like to dive into the configuration details alittle, read on.

Service Configuration DetailsNow let’s look at some of the details of how the default services are configured.As wasmentioned earlier, each service is defined by a section of the jabber.xml file, and eachsection is examined here.

Service XDBThe xdb (for XML database) component handles the data storage for jabberd. Its defaultbehavior is to store data in XML files in the ./spool subdirectory under the jabberdhome directory. Listing 2.1 shows the default configuration from the default jabber.xmlfile.

Listing 2.1 XDB Configuration

<xdb id=”xdb”>

<host/>

<load>

<xdb_file>./xdb_file/xdb_file.so</xdb_file>

</load>

<xdb_file xmlns=”jabber:config:xdb_file”>

<spool><jabberd:cmdline flag=’s’>./spool</jabberd:cmdline></spool>

04 0672325365 CH02 1/21/05 2:22 PM Page 44

Page 62: Jabber Developer’s Handbook [Sams 2004]

45Service Configuration Details

</xdb_file>

</xdb>

This section defines an instance of the xdb service with the name (specified by the IDattribute) “xdb”.The second line, <host/>, tells the internal message router that thiscomponent will handle data handling requests from all “hosts” (including virtual hosts) inthis server.The <load> section identifies the location of the component’s implementa-tion; in this case it’s a Unix shared library.The <xdb_file> section contains the configu-ration information specific to the xdb_file component.The only configuration itemlisted here is the location of the XML data files (./spool is the default).This directory isrelative to the directory where jabberd was built, which is not necessarily the same as thedirectory in which you run jabberd. Because the jabberd:cmdline tag is used, this valuecan be overridden on the command line like this:

# jabberd –s /var/spool/jabber

This command tells the xdb_file component to store its data in /var/spool/jabberrather than in the default ./spool directory.

A couple other options can be useful in a heavily-loaded server. One of these controlsthe length of time that the xdb component will keep data in memory (cache it) to avoidhaving to re-read the files.The default behavior is to keep the data in memory forever(or until the server shuts down, anyway).This makes a small server fast, because after thedata is read in, the server never needs to re-read it. Of course, the server’s memory canhold only so much, so a server with many clients needs to purge its cache sometimes.The <timeout> value controls how long cached items are held in memory from thetime they are read in. It might be better to cache data items based on how recently theywere used, but that’s not how it works.

The other option that is useful in a heavily-loaded server is called <maxfiles>. Itcontrols the size of the internal hash table used by xdb to reference data sets. It defaultsto 509, which gives good performance in most cases.

Listing 2.2 provides an example of an xdb_file component configuration that usesthe <timeout> and <maxfiles> parameters. It tells the component to cache files for 2minutes (120 seconds) and to set the cache hash table size to 13.

Listing 2.2 A Customized XDB Configuration

<xdb id=”xdb”>

<host/>

<load>

<xdb_file>./xdb_file/xdb_file.so</xdb_file>

</load>

<xdb_file xmlns=”jabber:config:xdb_file”>

<spool><jabberd:cmdline flag=’s’>./spool</jabberd:cmdline></spool>

<timeout>120</timeout>

Listing 2.1 Continued

04 0672325365 CH02 1/21/05 2:23 PM Page 45

Page 63: Jabber Developer’s Handbook [Sams 2004]

46 Chapter 2 Installing and Configuring Jabber Software

<maxfiles>13</maxfiles>

</xdb_file>

</xdb>

The spool directory (./spool in Listing 2.2) will hold subdirectories for each virtualhost that the server is running as well as data stored by other components that use xdbto store their data.

Service ioThis service controls the Managed Input/Output (MIO) capabilities of the server. Itsconfiguration is used to set the global default connection and throughput parameters.Other components (the s2s and c2s components, in particular) can override these val-ues.The jabber.xml file that ships with the jabberd server contains several exampleconfigurations for the io service.The only section of it that is not commented out isthis:

<rate points=”5” time=”25”/>

The <rate> tag governs how may connections can be made from a particular host(IP address). In this example, each host could make five connections in 25 seconds beforeits connections were no longer accepted.At the end of 25 seconds, the offending hostcould make another five connections.

The <karma> section of the io service configuration is commented out in the defaultjabber.xml file and deserves some explanation because you’ll see it again in other con-figuration sections.As with the <rate> section, the <karma> section of the io service isthe default value for the other connection services and can be overridden by them.

Like rate, karma is a Jabber concept for detecting and controlling misuse of the serv-er.The idea is that if one client sends too much data, it could monopolize the server atthe expense of other clients. Each client connection is assigned a karma value and whena connection’s karma gets too low, its data are not serviced for some (configurable) peri-od of time. So the <karma> settings control the throughput on a connection, and the<rate> settings control the number of connections that can be made.The parametersthat control the karma calculation are listed in Table 2.3.

Table 2.3 Karma Parameters

Parameter Default Description

heartbeat 2 The period of time (in seconds) between each evaluation of a con-nection’s karma.

init 5 The initial karma value for each connection.

max 10 The maximum karma value that a connection can have.

Listing 2.2 Continued

04 0672325365 CH02 1/21/05 2:23 PM Page 46

Page 64: Jabber Developer’s Handbook [Sams 2004]

47Service Configuration Details

inc 1 The karma value is incremented by this amount every heartbeat inwhich there is no penalty.

dec 0 The karma value is decremented by this amount every heartbeat inwhich there is a penalty.

penalty -5 When the karma value decreases to zero, it is set to this (usually negative) value.

restore 5 When the karma value increases to zero, it is set to this (usually positive) value.

resetmeter 0 If 1 (true), when the karma value increases to zero, the byte quota isreset to its maximum.

This all seems very complicated; what does it amount to? The amount of data a clientcan send per second without risking penalty is equal to:

(100×karma)/heartbeat

The karma value starts at the value you provide for the <init> tag, so if you set initto 100 and heartbeat to 2, the throughput allowed before the MIO starts throttling theconnection is 5,000 bytes/second:

(100×100)/2 = 5000

As long as the data rate stays below that level, nothing strange happens.You can effec-tively disable the karma limiting feature by setting <dec> to 0.

You can also control which hosts will be allowed to connect to your server andwhich ones will not be allowed by using <allow> and <deny> tags in configuring the ioservice. Each of these tags can include an <ip> tag section, which lists an IP address thatis either allowed to connect, in the case of the <allow> tag, or not allowed, in the caseof the <deny> tag.This tag can also be used to allow or deny a range of addresses, if youuse the <mask> tag.The mask is a bit mask that identifies which bits of the <ip> addressare valid—much like a netmask. Listing 2.3 provides some examples.

Listing 2.3 Sample Allow and Deny Settings

<!-- Only allow local (loopback) connections -->

<allow><ip>127.0.0.0</ip><mask>255.255.255.255</mask></allow>

<!-- Only allow connections from the 192.168.1 network -->

<allow><ip>192.168.1.0</ip><mask>255.255.255.0</mask></allow>

<!-- Reject connections from a specific host -->

<deny><ip>192.168.1.23</ip><mask>255.255.255.255</mask></deny>

If you don’t list any allow or deny tags, then connections from any host will be accepted.If you include an allow tag, then only those listed hosts will be allowed to connect. If

Table 2.3 Continued

Parameter Default Description

04 0672325365 CH02 1/21/05 2:23 PM Page 47

Page 65: Jabber Developer’s Handbook [Sams 2004]

48 Chapter 2 Installing and Configuring Jabber Software

any hosts are included in both an accept list and a deny list, their connections will berejected.

Finally, the <ssl> tag is used to configure Secure Socket Layer encryption betweenthe server and its clients. If you’ve compiled the server with SSL enabled (by doing./configure --enable-ssl before the make) you can configure your server to use SSLto protect client socket connections.

<ssl>

<key ip=’192.168.1.1’>/path/to/cert_and_key.pem</key>

<key ip=’192.168.1.100’>/path/to/other/cert_and_key.pem</key>

</ssl>

This example configures the io service to use the key in /path/to/cert_and_key.pem to protect connections to IP address 192.168.1.1, and the key in/path/to/other/cert_and_key.pem to protect connections to IP address192.168.1.100. More details on SSL configuration and encryption are in Chapter 6,“Jabber Security.”

Service c2sThe c2s service manages connections between clients and the jabberd server. Listing 2.4shows the default configuration from the jabber.xml file.

Listing 2.4 The Default c2s Configuration

<service id=”c2s”>

<load>

<pthsock_client>./pthsock/pthsock_client.so</pthsock_client>

</load>

<pthcsock xmlns=’jabber:config:pth-csock’>

<authtime/>

<karma>

<init>10</init>

<max>10</max>

<inc>1</inc>

<dec>1</dec>

<penalty>-6</penalty>

<restore>10</restore>

</karma>

<ip port=”5222”/>

</pthcsock>

</service>

As you saw earlier, the name (or ID) of this service will be “c2s” and its code will beloaded from the shared library ./pthsock/pthsock_client.so.The configurationoptions specific to the c2s service start at this line:

<pthcsock xmlns=’jabber:config:pth-csock’>

04 0672325365 CH02 1/21/05 2:23 PM Page 48

Page 66: Jabber Developer’s Handbook [Sams 2004]

49Service Configuration Details

The first option is <authtime>, which can be used to specify how many seconds eachclient has from the time it connects to finish its authentication sequence.The default set-ting allows the client an unlimited amount of time. Partially authenticated clients couldmount a denial of service attack on the server by holding network sockets open, so apublic production server should probably have this value set to something reasonable(something less than 10 seconds seems safe).

By default, the c2s service does not override the io service’s <rate> setting, but doesoverride its <karma> settings.The throughput allowed on each client connection istherefore 500bps:

(10×100)/2 = 500

It’s 500bps because the value of <init> is set to 10, and the value of <heartbeat> isunset and defaults to 2.

As mentioned earlier, the <ip> section defines the TCP/IP port on which the servicewill listen for connections.The <ssl> tag can be used in addition to the <ip> tag todirect the service to use SSL-protected socket connections. Here’s an example:

<ssl port=’5223’>127.0.0.1</ssl>

<ssl port=’5224’>192.168.1.100</ssl>

These entries tell the c2s service to listen on port 5223 for SSL loopback connec-tions and on port 5224 for remote SSL connections. Unlike the <ip> tag, with the<ssl> tag you must list the IP address or no connections will be established.

The <alias> tag enables the server’s clients to refer to the server by different names.This can be useful if your server has multiple names like myserver and myserver.mydomain.com and you want clients to be able to use the names interchangeably.Thissection configures the server to consider all packets addressed to myserver to beaddressed to myserver.mydomain.com:

<ip port=”5222”/>

<alias to=”myserver.mydomain.com”>myserver</alias>

Service s2sThe s2s service manages connections to another Jabber server. Its configuration is verysimilar to the c2s service.You can see the s2s configuration that comes with the jabberddistribution in Listing 2.5.

Listing 2.5 The Default s2s Configuration

<service id=”s2s”>

<load>

<dialback>./dialback/dialback.so</dialback>

</load>

<dialback xmlns=’jabber:config:dialback’>

<legacy/>

<ip port=”5269”/>

04 0672325365 CH02 1/21/05 2:23 PM Page 49

Page 67: Jabber Developer’s Handbook [Sams 2004]

50 Chapter 2 Installing and Configuring Jabber Software

<karma>

<init>50</init>

<max>50</max>

<inc>4</inc>

<dec>1</dec>

<penalty>-5</penalty>

<restore>50</restore>

</karma>

</dialback>

</service>

The format looks pretty familiar by now.This service’s name will be “s2s” and the codewill be loaded from the shared library ./dialback/dialback.so. It’s called “dialback”because the protocol between servers requires the server that is receiving the message tocontact the sending server (that is, dial back) to help detect a server that is claiming to beone host, but is really another. More details on the dialback protocol are in Chapter 6.

Looking now within the <dialback> section, the <legacy> tag tells the server tomaintain backward compatibility with older servers. Not shown in this example is the<maxhosts> tag, which controls the size of the hash table used to store contact informa-tion about other hosts.The default value for <maxhosts> is 997, which doesn’t meanthat this server can communicate with only 997 other servers, but just that the internalhash tables will have 997 buckets.

Also not shown are two timeout values that control how long socket connections arehandled between servers. If two servers send lots of messages to each other, it makessense to just keep a connection open rather than opening and closing one every time amessage is sent.The <idletimeout> value controls how many seconds a connection willbe kept open after the last packet has been sent through it. It defaults to 900 seconds (15minutes).The <queuetimeout> tag controls how many seconds this server will hangonto a packet destined for another server, if that server is unavailable. It defaults to 30seconds.

As with the c2s service, the s2s service can override the io service’s values for<rate> and <karma>. In the default example earlier, only the <karma> is overridden.Using the formula for karma calculation:

(50×100)/2 = 2500

the throughput for this connection will be limited to 2500 bytes per second.The s2scomponent usually has a higher karma maximum than the c2s component, because sev-eral clients’ data may be flowing through it.

As mentioned earlier, the <ip> section defines the TCP/IP port on which the servicewill listen for connections.The last unshown option is the secret attribute.The s2scomponent uses the secret string as input to the dialback protocol. If you don’t specify

Listing 2.5 Continued

04 0672325365 CH02 1/21/05 2:23 PM Page 50

Page 68: Jabber Developer’s Handbook [Sams 2004]

51Service Configuration Details

one, it generates a random string, which is probably better anyway.You specify secret asan attribute to the <dialback> tag, not as its own element, as shown in Listing 2.6.

Listing 2.6 Specifying a Secret for s2s Connections

<service id=”s2s”>

<load>

<dialback>./dialback/dialback.so</dialback>

</load>

<dialback xmlns=’jabber:config:dialback’ secret=’123456’>

… the rest as before …

When configured this way, the server uses 123456 in the dialback protocol rather thangenerate a random string.

Service dnsrvThe dnsrv service handles requests to lookup IP addresses given host names. Its defaultconfiguration looks like Listing 2.7.

Listing 2.7 Default dnserv Configuration

<service id=”dnsrv”>

<host/>

<load>

<dnsrv>./dnsrv/dnsrv.so</dnsrv>

</load>

<dnsrv xmlns=”jabber:config:dnsrv”>

<resend service=”_jabber._tcp”>s2s</resend>

<resend>s2s</resend>

</dnsrv>

</service>

This configuration tells the dnsrv component to try two different methods to resolvehostnames: Look in the Domain Name Service (DNS) for an SRV record for a servicecalled jabber accessible over TCP and, failing that, just look up the hostname by usingthe operating system’s default host lookup mechanism and send it to the s2s componentthere. Setting up DNS SRV records is outside the scope of this book, but you can getdetails about it from Internet RFC 2782. Unless you’re building a particularly large andcomplicated server constellation, you’ll probably never have to change any of this.

Log ServicesComponents running within the Jabber server can create log messages to help diagnoseproblems and keep track of how the server is used.A log message can be tagged as alert,

04 0672325365 CH02 1/21/05 2:23 PM Page 51

Page 69: Jabber Developer’s Handbook [Sams 2004]

52 Chapter 2 Installing and Configuring Jabber Software

notice, record, warn, or it can have no tag.A logger can be configured to handle one ormore of these types of log messages.The default jabber.xml configuration includes twologgers: the rlogger, which is configured to handle “record” log messages, and theelogger (error logger) which is configured to handle all other log messages. Listing 2.8shows the default configuration of the rlogger.

Listing 2.8 Default Configuration of the rlogger

1: <log id=’rlogger’>

2: <host/>

3: <logtype>record</logtype>

4: <format>%d %h %s</format>

5: <file>record.log</file>

6: </log>

Line 1 declares that this logger is called rlogger. Line 2 of this configuration says thatrlogger will handle log entries from any host. Of course, this logger is unlikely to seelog messages for other hosts.

Line 3 says that this logger handles only log entries tagged record—it ignores all oth-ers and lets other loggers handle them. Line 4 defines the message’s format. Four parts ofthe log record are available to be included in the message:

n %d—The time that the message was createdn %h—The name of the host that created the messagen %t—The log message tag (record, for example)n %s—The text of the log message

Because the message tag is always record, the rlogger example prints just the date, host,and message for each log record. It prints entries that look like this:

20021102T20:50:35 jim@my-jabber login ok 192.168.1.2 jim

It’s nice to be able to change the format of the log messages to make it easy forscripts to parse the log file.

Finally, line 6 says that the logger output should go in the file record.log.The elogger is configured in the same manner as the rlogger. Its default configura-

tion looks like Listing 2.9.

Listing 2.9 Default Configuration of the elogger

1: <log id=’elogger’>

2: <host/>

3: <logtype/>

4: <format>%d: [%t] (%h): %s</format>

5: <file>error.log</file>

6: <stderr/>

7: </log>

04 0672325365 CH02 1/21/05 2:23 PM Page 52

Page 70: Jabber Developer’s Handbook [Sams 2004]

53Service Configuration Details

This configuration looks a lot like the rlogger, with a couple of differences. First, the<logtype> tag is empty.This means that this logger handles all records that are not han-dled by some other logger. So, in this case, it handles all records except those taggedrecord.Those are handled by the rlogger.

The format of the output records is a little different, too (line 4). Here it also outputsrecord type [%t] and some formatting characters to make it more readable.The outputfile is called error.log (line 5) and it also outputs these records to the standard errorstream (line 6).An output line from this logger looks something like this:

20021102T20:48:52: [warn] (io_select): 192.168.1.1(13) is being

➥ connection rate limited

The Sessions Service Last but not least in this tour of the default jabberd components is the sessions service,also known as the Jabber Session Manager or JSM.This service is responsible for han-dling most of the server’s instant messaging functions. It handles the registration of newclients, message storage for clients that happen to be offline, message filtering, and so on.Compared to the other services covered so far, it’s a real monster. It’s composed of sever-al sub-services that each perform a manageable chunk of functionality.You can see thedefault XML configuration for the sessions service from the jabber.xml file in Listing 2.10.

Listing 2.10 Default Configuration of the JSM

1 : <service id=”sessions”>

2 : <host><jabberd:cmdline flag=”h”>localhost</jabberd:cmdline></host>

3 :

4 : <jsm xmlns=”jabber:config:jsm”>

5 :

6 : <filter>

7 : <default/>

8 : <max_size>100</max_size>

9 : <allow>

10 : <conditions>

11 : <ns/>

12 : <unavailable/>

13 : <from/>

14 : <resource/>

15 : <subject/>

16 : <body/>

17 : <show/>

18 : <type/>

19 : <roster/>

20 : <group/>

21 : </conditions>

04 0672325365 CH02 1/21/05 2:23 PM Page 53

Page 71: Jabber Developer’s Handbook [Sams 2004]

54 Chapter 2 Installing and Configuring Jabber Software

22 : <actions>

23 : <error/>

24 : <offline/>

25 : <forward/>

26 : <reply/>

27 : <continue/>

28 : <settype/>

29 : </actions>

30 : </allow>

31 : </filter>

32 :

33 : <vCard>

34 : <FN>Jabber Server</FN>

35 : <DESC>A Jabber Server!</DESC>

36 : <URL>http://foo.bar/</URL>

37 : </vCard>

38 :

39 : <register notify=”yes”>

40 : <instructions>Choose a username and password to register

with this server.</instructions>

41 : <name/>

42 : <email/>

43 : </register>

44 :

45 : <welcome>

46 : <subject>Welcome!</subject>

47 : <body>Welcome to the Jabber server at localhost -- we hope you

enjoy this service! For information about how to use Jabber,

visit the Jabber User’s Guide at http://docs.jabber.org/

</body>

48 : </welcome>

49 :

50 : <!--

51 : <admin>

52 : <read>support@localhost</read>

53 : <write>admin@localhost</write>

54 : <reply>

55 : <subject>Auto Reply</subject>

56 : <body>This is a special administrative address. Your

message was received and forwarded to server

administrators.

</body>

57 : </reply>

58 : </admin>

59 : -->

Listing 2.10 Continued

04 0672325365 CH02 1/21/05 2:23 PM Page 54

Page 72: Jabber Developer’s Handbook [Sams 2004]

55Service Configuration Details

60 :

61 : <update><jabberd:cmdline flag=”h”>localhost</jabberd:cmdline></update>

62 :

63 : <vcard2jud/>

64 :

65 : <browse>

66 : <service type=”jud” jid=”users.jabber.org”

name=”Jabber User Directory”>

67 : <ns>jabber:iq:search</ns>

68 : <ns>jabber:iq:register</ns>

69 : </service>

70 :

71 : </browse>

72 :

73 : </jsm>

74 :

75 : <load main=”jsm”>

76 : <jsm>./jsm/jsm.dll</jsm>

77 : <mod_echo>./jsm/jsm.dll</mod_echo>

78 : <mod_roster>./jsm/jsm.dll</mod_roster>

79 : <mod_time>./jsm/jsm.dll</mod_time>

80 : <mod_vcard>./jsm/jsm.dll</mod_vcard>

81 : <mod_last>./jsm/jsm.dll</mod_last>

82 : <mod_version>./jsm/jsm.dll</mod_version>

83 : <mod_announce>./jsm/jsm.dll</mod_announce>

84 : <mod_agents>./jsm/jsm.dll</mod_agents>

85 : <mod_browse>./jsm/jsm.dll</mod_browse>

86 : <mod_admin>./jsm/jsm.dll</mod_admin>

87 : <mod_filter>./jsm/jsm.dll</mod_filter>

88 : <mod_offline>./jsm/jsm.dll</mod_offline>

89 : <mod_presence>./jsm/jsm.dll</mod_presence>

90 : <mod_auth_plain>./jsm/jsm.dll</mod_auth_plain>

91 : <mod_auth_digest>./jsm/jsm.dll</mod_auth_digest>

92 : <mod_auth_0k>./jsm/jsm.dll</mod_auth_0k>

93 : <mod_log>./jsm/jsm.dll</mod_log>

94 : <mod_register>./jsm/jsm.dll</mod_register>

95 : <mod_xml>./jsm/jsm.dll</mod_xml>

96 : </load>

97 :

98 : </service>

Let’s take this a piece at a time. Line 1 identifies this as the sessions service, and line 2says that this service handles messages to localhost (unless you override it on the com-mand line with the –h option).As was explained earlier, you’ll probably change this toyour server’s actual name.

Listing 2.10 Continued

04 0672325365 CH02 1/21/05 2:23 PM Page 55

Page 73: Jabber Developer’s Handbook [Sams 2004]

56 Chapter 2 Installing and Configuring Jabber Software

Filters

The real configuration of the JSM starts with line 4 with the <jsm> tag. Next, startingon line 6, is the configuration for the message filtering service.This service can be usedto change messages within the server before they make their way to the client. In theinstant messaging context, you might use this to block people you don’t want to talkwith or reply with a vacation message when you’re away.

A filter is a set of conditions and a set of actions. If any of the conditions are met,then all the actions are taken and no more filters are evaluated.The <allow> section ofthe filter configuration (starting at line 9) lists the set of conditions and actions that canbe used to make filters.Their meanings are summarized in Tables 2.4 and 2.5.

Table 2.4 Message Filter Conditions

Condition Example Meaning

<ns>jabber:iq:time</ns> Matches iq packets with the specified XMLnamespace.

<unavailable/> Matches whether the recipient’s presence status isunavailable.

<from>me@my-jabber</from> Matches packets from the specified sender.

<resource>Work</resource> Matches the recipient’s resource (Work in thiscase).

<subject>Hi There</subject> Matches whether the subject is exactly as speci-fied.

<body>Some message text.</body> Matches whether the body of the message isexactly as specified.

<show>xa</show> Matches the recipient’s presence show tag.Generally this is one of xa, normal, chat, ordnd.

<type>normal</type> Matches the type of the message. Generally this isone of normal, chat, headline, or error.

<roster/> Matches whether the sender is in the recipient’sroster.

<group>Friends</group> Matches whether the sender is in the specifiedgroup in the recipient’s roster.

Table 2.5 Message Filter Actions

Action Example Meaning

<error>Error Message!</error> Replies with an error message containing thespecified text.

<offline/> Stores the message offline.

<forward>me@my-jabber</forward> Forwards the message to the specified JID.

04 0672325365 CH02 1/21/05 2:23 PM Page 56

Page 74: Jabber Developer’s Handbook [Sams 2004]

57Service Configuration Details

<reply>Thanks!</reply> Sends a reply message to the sender.

<settype>headline</settype> Changes the type of the message to be as speci-fied.

<continue/> A special action that tells the filter processor tokeep processing rules, even though this onematched.

The first element in the <filter> configuration (line 7) is called <default> and it’sempty in the stock jabberd configuration. If there are filters in this section, they areapplied to every message. So if a mean server administrator wanted to discourage chat-ting, he could define a default filter like this:

<default>

<rule name=”pointy-haired”>

<type>chat</type>

<error>Get Back To Work!</error>

</rule>

</default>

This would match all chat messages and reply with an error.

The Server vCard

The section starting at line 33 in Listing 2.10 defines the default identity information forthe server. Clients can query the server to ask for its vCard information, so you mightwant to change this to be more descriptive than the default—maybe something like this:

<vCard>

<FN>Global Mega Corp Jabber Server</FN>

<DESC>Jabber server for internal use only!</DESC>

<URL>http://www.globalmegacorp.com/</URL>

</vCard>

New Client Registration

The section starting at line 39 in Listing 2.10 defines the information required to regis-ter a new client with this server.

<register notify=”yes”>

<instructions>Choose a username and password to register.</instructions>

<name/>

<email/>

</register>

The notify=”yes” attribute tells the registration module to send a message to theadministrator (if there is one) whenever a new client registers.Administrators are defineda little farther down in the jabber.xml file.

Table 2.5 Continued

Action Example Meaning

04 0672325365 CH02 1/21/05 2:23 PM Page 57

Page 75: Jabber Developer’s Handbook [Sams 2004]

58 Chapter 2 Installing and Configuring Jabber Software

The JSM always requires a username and a password, but this configuration alsorequires a name and an email address. If you want to disallow any new users, you cancomment out this section of the jabber.xml file.The current crop of instant messageclients seem to universally ignore this information.

New Client Welcome

The next section of the JSM configuration in Listing 2.10 (starting at line 45) defines amessage that is sent to all new users. Here’s another example:

<welcome>

<subject>Welcome!</subject>

<body>Welcome to the Global Mega Corp Jabber server</body>

</welcome>

Server Administration

The section starting at line 50 in the default configuration in Listing 2.10 is commentedout.When uncommented, it enables you to define users that will have “administrative”rights to the JSM.They can’t really change the configuration as you can by editing thejabber.xml file, but they can do things such as get the list of users, send a message to allusers, and so on.

You can list two types of administrative users: those with read-only administrativeaccess, and those with read/write access. If you look again at the configuration, you cansee two administrative users:

<admin>

<read>support@localhost</read>

<write>admin@localhost</write>

<reply>

<subject>Auto Reply</subject>

<body> Message was received and forwarded to server administrators.</body>

</reply>

</admin>

The user support@localhost has read-only administrative access, so he can receiveadministrative messages and see who’s online.The user admin@localhost has read/writeaccess, so he can also send administrative messages. Naturally, if you uncomment the<admin> section, you need to change these JIDs.

All administrative users get messages sent to the server without a username in theaddress.That is, the “to” address is just the server name with no @ and user name beforeit.Also, any administrative user can get a list of the currently connected clients by send-ing an IQ packet like this:

<iq type=’get’ to=’my-server-name/admin’>

<query xmlns=’jabber:iq:browse’/>

</iq>

04 0672325365 CH02 1/21/05 2:23 PM Page 58

Page 76: Jabber Developer’s Handbook [Sams 2004]

59Service Configuration Details

If the sender is an authorized administrator, a document like this will be returned:

<iq type=’result’ to=’admin@my-server-name/winjab’ from=’my-server-name/admin’>

<item jid=’my-server-name/admin’

name=’Online Users (seconds, sent, received)’

xmlns=’jabber:iq:browse’>

<user jid=’user1@my-server-name’ name=’user1 (130, 10, 8)’/>

<user jid=’winjab@my-server-name’ name=’winjab (122, 15, 11)’/>

<user jid=’me2@my-server-name’ name=’me2 (6, 11, 7)’/>

</item>

</iq>

The numbers in parentheses after each user are, respectively,n The number of seconds the client has been connectedn The number of packets received from that clientn The number of packets sent to that client

Administrative users with read/write privileges (that is, those that are contained in a<write> tag) can also send broadcast messages.These can be sent to all users when theylog in (the message of the day or MOTD) or to all currently online users.

A message formatted like this goes to all online users:

<message to=”my-server-name/announce/online”>

<body>This is a broadcast message!</body>

</message>

A message like this is sent to all online users and also to all users when they log in:

<message to=”my-server-name/announce/motd”>

<body>The message of the day.</body>

</message>

The Update Section

The JSM can be configured to ask another jabberd server whether a new version of thejabberd software is available.A central repository of this information is kept on a serverat update.jabber.org.The following line specifies that the update server should let usknow if there is a new version of the JSM available:

<update><jabberd:cmdline flag=”h”>my-server-name</jabberd:cmdline></update>

For this to work, your server has to be able to reach update.jabber.org, andupdate.jabber.org has to be able to reach your server.There’s nothing really importantlost by commenting this out.

vCard Synchronization

Each client can store information about itself in its own vCard. Client information canalso be stored in the Jabber User Directory (JUD). Including the <vcard2jud> line, as inline 63 of Listing 2.10, instructs the JSM to send new and changed vCard information tothe JUD.This can be safely commented out.

04 0672325365 CH02 1/21/05 2:23 PM Page 59

Page 77: Jabber Developer’s Handbook [Sams 2004]

60 Chapter 2 Installing and Configuring Jabber Software

Service Browsing

This section (starting at line 65 in Listing 2.10) specifies how the JSM will representservices that clients can browse.This is how a Jabber server advertises its capabilities andthe capabilities of related services.The default configuration advertises the global JabberUser Directory (JUD) on the users.jabber.org server. Lines 67 and 68 declare thatthis service can respond to search (the jabber:iq:search namespace) and register (thejabber:iq:register) IQ requests. Clients can use the information in the <browse>section to determine how to interact with the service and, if appropriate, what informa-tion to display to a user.You can find more details about service browsing in Chapter 5.

Component Configuration

As mentioned above, the JSM service is made up of several different components.Thesecomponents are defined in lines 75 through 96 of Listing 2.10.The order of the modulesis important, because packets are delivered to modules in the order in which they arelisted, and you can disable individual modules by commenting them out of the <load>section.Table 2.6 summarizes each one.

Table 2.6 JSM Components

JSM Module Service

mod_echo Replies with the same message it was given. It replies to messages sentto servername/echo.

mod_roster Handles users’ rosters.

mod_time Handles requests for the server’s time of day.

mod_vcard Manages users’ vCard information.

mod_last Keeps track of the last time a user logged out.

mod_version Responds with the server’s version information.

mod_announce Handles the broadcast messages that can be sent by administrators.

mod_agents Maintains backward compatibility with an old method of servicebrowsing.

mod_browse Handles the information defined in the <browse> section describedearlier.

mod_admin Handles the features available to administrative users.

mod_filter Filters messages as described earlier.

mod_offline Stores messages for clients that are not connected to the server.

mod_presence Handles clients’ presence and availability status information.

mod_auth_plain Handles simple plaintext password login authentication.

mod_auth_digest Handles hashed password authentication.

mod_auth_0k Handles “zero-knowledge” authentication. See Chapter 6,“JabberSecurity,” for details about authentication.

04 0672325365 CH02 1/21/05 2:23 PM Page 60

Page 78: Jabber Developer’s Handbook [Sams 2004]

61Common Optional Services

mod_log Logs the end time of users’ sessions.

mod_register Enables new client registration and manages clients’ registration infor-mation.

mod_xml Provides clients a mechanism for storing data on the server.

The configuration of the JSM is by far the biggest section of the default jabber.xml fileand its capabilities make up the core of the server’s instant messaging capabilities.

Common Optional ServicesThis section looks briefly at how to configure two common services used to supportinstant messaging: the Jabber User Directory and the conferencing components.

Jabber User Directory (JUD)As you saw in the default jabber.xml configuration, there is an instance of the JUDrunning at users.jabber.org that can be accessed from anywhere.You may, however,want to set up your own JUD for use within your own group. It’s easy to install andconfigure.

NoteMany of the optional components have multiple implementations. This chapter covers the C versions, but

others are available from various sources including http:/www.jabberstudio.org.

First, download the installation package from the same place you got the jabberd server:http://jabberd.jabberstudio.org/downloads.As of this writing, the current versionof JUD is 0.4.

Because the JUD code refers to code in the jabberd code base, unpack the code intothe same directory as your jabberd installation. Unpacking the software creates a newdirectory called jud-0.4 containing the C source code. Simply cd into that directoryand type make:

$ cd ./jud-0.4

$ make

gcc -g -Wall -I. -I. -I/usr/local/jabber-1.4.2/jabberd/pth-1.4.0 -fPIC

➥ -I../jabberd -c -o jud.o jud.c

In file included from jud.c:31:

jud.h:33:1: warning: “VERSION” redefined

In file included from jud.h:31,

from jud.c:31:

../jabberd/jabberd.h:47:1: warning: this is the location of the

➥ previous definition

gcc -g -Wall -I. -I. -I/usr/local/jabber-1.4.2/jabberd/pth-1.4.0

Table 2.6 Continued

JSM Module Service

04 0672325365 CH02 1/21/05 2:23 PM Page 61

Page 79: Jabber Developer’s Handbook [Sams 2004]

62 Chapter 2 Installing and Configuring Jabber Software

➥ -fPIC -I../jabberd -c -o jud_reg.o jud_reg.c

In file included from jud_reg.c:31:

jud.h:33:1: warning: “VERSION” redefined

In file included from jud.h:31,

from jud_reg.c:31:

../jabberd/jabberd.h:47:1: warning: this is the location of the

➥ previous definition

gcc -g -Wall -I. -I. -I/usr/local/jabber-1.4.2/jabberd/pth-1.4.0

➥ -fPIC -I../jabberd -c -o jud_search.o jud_search.c

In file included from jud_search.c:31:

jud.h:33:1: warning: “VERSION” redefined

In file included from jud.h:31,

from jud_search.c:31:

../jabberd/jabberd.h:47:1: warning: this is the location of the

➥ previous definition

gcc -g -Wall -I. -I. -I/usr/local/jabber-1.4.2/jabberd/pth-1.4.0

➥ -fPIC -I../jabberd -shared -o jud.so jud.o jud_reg.o jud_search.o

➥ -ldl –lresolv

You might see some warnings about “VERSION” being redefined, but they can besafely ignored.After this finishes, a new shared library is in the jud-0.4 directory.This isthe module that you load by using jabber.xml.

Next, add a service section like this to jabber.xml:

<service id=”jud”>

<host>jud.my-jabber</host>

<load><jud>./jud-0.4/jud.so</jud></load>

<jud xmlns=”jabber:config:jud”>

<vCard>

<FN> Global Mega Corp Directory</FN>

<DESC>This service provides a simple user directory service.</DESC>

<URL>http://foo.bar/</URL>

</vCard>

</jud>

</service>

Be sure to replace the contents of the <host> tag following “jud” with your servername.You can also change the vCard information as appropriate.

This adds the service, but it would be good for clients to be able to find the serviceby browsing, so you need to add some information to the JSM configuration. Find thesection contained in the <browse> tags and add this entry:

<service type=”jud” jid=”jud.my-jabber” name=”Global Mega Corp Directory”>

<ns>jabber:iq:search</ns>

<ns>jabber:iq:register</ns>

</service>

The jid attribute in the <service> tag matches the contents of the <host> tag inthe service definition.The JUD service responds to search and register requests, so the

04 0672325365 CH02 1/21/05 2:23 PM Page 62

Page 80: Jabber Developer’s Handbook [Sams 2004]

63Common Optional Services

jabber:iq:search and jabber:iq:register namespaces are included with the <ns>tags.

That’s all there is to it. Restart jabberd and you should be able to use the new service.If you browse to the Jabber server with the Winjab browser, you see what appears inFigure 2.3.

Figure 2.3 Browsing the Jabber server.

Clicking on the icon in the right pane browses into the JUD service, and you see a win-dow that looks like Figure 2.4.

Figure 2.4 Browsing the JUD service.

The left pane now contains links that enable you to use the search and register capabili-ties of the JUD.

ConferencingThe conference service component is also available at http://jabberd.jabberstudio.org/downloads.As of this writing, the current version of the conferencecomponent is 0.4.

04 0672325365 CH02 1/21/05 2:23 PM Page 63

Page 81: Jabber Developer’s Handbook [Sams 2004]

64 Chapter 2 Installing and Configuring Jabber Software

Unpack the archive into the same directory with the jabberd software. It creates anew directory called conference-0.4. Change (cd) into that directory and type maketo build it.The build should look something like this:

$ cd./conference-0.4

$ make

gcc -g -Wall -I. -I. -I/usr/local/jabber-1.4.2/jabberd/pth-1.4.0

➥ -fPIC -I../jabberd -c -o conference.o conference.c

In file included from conference.c:31:

conference.h:33:1: warning: “VERSION” redefined

In file included from conference.h:31,

from conference.c:31:

../jabberd/jabberd.h:47:1: warning: this is the location of the

➥ previous definition

gcc -g -Wall -I. -I. -I/usr/local/jabber-1.4.2/jabberd/pth-1.4.0

➥ -fPIC -I../jabberd -c -o conference_room.o conference_room.c

In file included from conference_room.c:31:

conference.h:33:1: warning: “VERSION” redefined

In file included from conference.h:31,

from conference_room.c:31:

../jabberd/jabberd.h:47:1: warning: this is the location of the

➥ previous definition

gcc -g -Wall -I. -I. -I/usr/local/jabber-1.4.2/jabberd/pth-1.4.0

➥ -fPIC -I../jabberd -c -o conference_user.o conference_user.c

In file included from conference_user.c:31:

conference.h:33:1: warning: “VERSION” redefined

In file included from conference.h:31,

from conference_user.c:31:

../jabberd/jabberd.h:47:1: warning: this is the location of the

➥ previous definition

gcc -g -Wall -I. -I. -I/usr/local/jabber-1.4.2/jabberd/pth-1.4.0

➥ -fPIC -I../jabberd -shared -o conference.so conference.o

➥ conference_room.o conference_user.o -ldl -lresolv

$

The build creates a new shared library in the ./conference-0.4 directory that wecan load into the server by using the jabber.xml file.

As with the JUD, you need to add a <service> section for the conferencing compo-nent. It should look something like this:

<service id=’conference.my-jabber’>

<load><conference>./conference-0.4/conference.so</conference></load>

<host>conference.my-jabber</host>

<conference xmlns=”jabber:config:conference”>

<public/>

<vCard>

<FN>Public Chatrooms</FN>

<DESC>This service is for public chatrooms.</DESC>

<URL>http://foo.bar/</URL>

</vCard>

04 0672325365 CH02 1/21/05 2:23 PM Page 64

Page 82: Jabber Developer’s Handbook [Sams 2004]

65Common Optional Services

<history>20</history>

<notice>

<join> has become available</join>

<leave> has left</leave>

<rename> is now known as </rename>

</notice>

</conference>

</service>

A couple points of configuration data bear explanation. First, within the <conference> tag, there is a <public/> element.This means that users can browse forrooms within the conference service. If you were to replace it with <private/>, thenusers would not be able to browse rooms and would have to know about rooms beforethey could join them.

Second, the vCard section is the vCard of the conference component.The value inthe <history> tag is the number of messages saved by the server so that clients thatenter the room can see the past context of the conversation. In this case, 20 messages aresaved and replayed for clients when they enter the room. Not shown is another availabletag, <maxrooms>, which controls the maximum number of conference rooms that can becreated.

Although rooms are created whenever a client requests to enter a room that doesn’texist, it’s also possible to create rooms in the server configuration by adding an entry tothe conference configuration like this:

<room jid=”[email protected]”>

<name>Preconfigured Room</name>

</room>

In addition to the <name> tag shown here, you can also set several other properties ofthe room:

n <notify>—Override the entry/exit/rename messages using <join>, <leave>, and<rename> elements.

n <nick>—Require that all clients use a nickname.n <secret>—Prompt clients for a secret password before they are allowed access to

the room.n <privacy>—If present, this tag indicates that the clients’ real JIDs should be hid-

den from the other members of the room.

Browsing the conferencing service is handled a little differently by the JSM than theother services. It has its own tag type, so rather than adding a <service> section, add aline like this to the <browse> section of the JSM configuration:

<conference type=”public” jid=”conference.my-jabber” name=”Public Chatrooms”/>

This advertises the conference service to browsing clients. Now, if you restart jabberdand look at the Winjab browser, you can see the conferencing component, as shown inFigure 2.5.

04 0672325365 CH02 1/21/05 2:23 PM Page 65

Page 83: Jabber Developer’s Handbook [Sams 2004]

66 Chapter 2 Installing and Configuring Jabber Software

Figure 2.5 More services to browse.

Clicking on the Public Chatrooms icon in the right pane takes you to the JUD service,which looks like Figure 2.6.

Figure 2.6 Browsing the Conference service.

The left pane now contains a link that lets you join the conference.

Instant Messaging ClientsJabber was designed for instant messaging, so even if you’re not doing instant messaging,the clients can be useful debugging tools. One of the best clients for analyzing the datafrom a server is WinJab. It is an open-source (that is, free) client for Windows computers.It can be downloaded from http://winjab.sourceforge.net. It installs like mostWindows applications and puts an icon on the desktop that you can use to start it.

04 0672325365 CH02 1/21/05 2:23 PM Page 66

Page 84: Jabber Developer’s Handbook [Sams 2004]

67Instant Messaging Clients

NoteThe next version of WinJab is called Exodus. Exodus looks much the same as WinJab, but has a few new

features.

When WinJab starts, it presents a screen to let you specify the properties you’d like touse, including what username you’ll use and to what server you will connect. Figure 2.7shows the initial configuration screen.

Figure 2.7 WinJab configuration.

Click OK to log in to the server.The next screen is the main messaging screen.Your ros-ter appears on the left, and any messages you have received appear on the right.

We did most of the XML debugging for this book with the Debug XML pane inWinJab. Pull down the Tools menu and select Show Debug XML to open up the panelshown in Figure 2.8.All messages to and from the server are printed in raw XML here.You can also click the Send XML button to send arbitrary XML to the server.This isespecially useful for debugging services.

Figure 2.8 Debugging XML with WinJab.

04 0672325365 CH02 1/21/05 2:23 PM Page 67

Page 85: Jabber Developer’s Handbook [Sams 2004]

68 Chapter 2 Installing and Configuring Jabber Software

This example is just a simple exchange of messages, but it shows every detail of theXML documents.

Summary This chapter has detailed setting up and configuring the Jabber service. By now, youshould be able to install and set up services and have at your disposal a running jabberd.Understanding how to manipulate and augment the jabber.xml configuration file isimportant if you want to add capability to Jabber, which is where we are heading inChapter 5.

First, however, you need to complete the other side of the picture by understandingthe construction of Jabber clients and how clients communicate with the server (usingthe Jabber Client Protocol), which is covered in Chapter 3.

04 0672325365 CH02 1/21/05 2:23 PM Page 68

Page 86: Jabber Developer’s Handbook [Sams 2004]

3All About Jabber Clients

A client is to me a mere unit, a factor in a problem.

Sherlock Holmes

You can learn a lot from the client. Some 70% doesn’t matter, but that 30% will kill you.”

Paul J Paulson, former President, Doyle Dane Bernbach

THIS CHAPTER LOOKS AT THE OTHER SIDE of the conversation: the client—the reasonthat a server exists in the first place. In the second quote, the president of the once pow-erful DDB&O Advertising Agency seems to be saying that there’s some absolute mini-mum that you must implement in a relationship with (human) advertising clients. Incontrast, there is no minimum that you need to implement for a successful Jabber client.The Jabber client can be pretty simple—anything that correctly responds to some (or all)of the set of XML-based Jabber messages. It can be pretty complex, as are the GUIclients that are commonly available. No rule says how much of the entire Jabber protocola client must implement.

Implement exactly as much as you need to provide the functionality of your applica-tion, and ignore (gracefully) the rest. For example, the XML-RPC tunnel shown inChapter 7,“Jabber and Web Services,” doesn’t implement any message support, nor doesit respond to any presence messages. It expects only <iq> (infoquery) messages contain-ing XML-RPC requests and responses, transported for clients that are not actually Jabberclients.The picture sharing application shown later in this chapter is a Jabber client thatresponds to all three types of Jabber messages, but which renders message payloads in aspecial way.

05 0672325365 CH03 1/21/05 2:23 PM Page 69

Page 87: Jabber Developer’s Handbook [Sams 2004]

70 All About Jabber Clients

Finally, what’s exciting about Jabber is that you can implement elements of your clientin any language that pleases you—Jabber delivers on its promise to be language and plat-form neutral.

This chapter gives you some rudimentary Jabber clients to build; these will help youunderstand that creating a client is really a pretty easy exercise.Then it presents a com-prehensive reference to the types of messages your client might expect to handle. Finally,it explores the various stanzas of XML used in Jabber messaging.

What Is a Jabber Client?A design goal for Jabber was that it support a simple messaging structure and allowclients ranging from full function GUIs and special applications, down to the triviallysimple (for example, even something as simple as a Telnet connection on the jabberdport, as you will see shortly in the first examples in this chapter).

Architecturally, Jabber imposes very few restrictions on clients.The only things aJabber client really has to do are

n Communicate with one Jabber server over TCP sockets.n Parse and interpret XML “stanzas” over an XML stream. (The meaning of “XML

Stream” is explained shortly.) n Understand a small set of core Jabber XML message types.

Session MechanicsIt’s always been our opinion that the closer to the socket layer an application can get, themore efficient your application can be.A Jabber session is really just pairs of fullduplexed sockets—one at the Jabber server for each client, one at each of the clients.(Actually, it’s better to think in terms of endpoints rather than clients because endpointscan be anything from traditional conversational client GUIs to Internet services.)

The down side is that you will have to do a lot more management of the conversa-tion. By custom, a Jabber session is a TCP session on port 5222 (or 5223 if SSL encryp-tion is turned on).As Figure 1.1 shows, a session is in effect until the socket is closed byone of the parties.

A session can be conceived as a bi-directional stream of XML constantly being ana-lyzed for content and correctness by either clients or servers. Structurally, a client really isan XML stream reader in the manner as a typical application built on an XML SAX orstream parser, where the application’s actions are event driven—events being triggered bytokens in the received XML stream.

Protocol MechanicsAs we have talked about before, within the context of XML messaging, Jabber’s openXML protocol contains only three top-level XML elements:

05 0672325365 CH03 1/21/05 2:23 PM Page 70

Page 88: Jabber Developer’s Handbook [Sams 2004]

71Protocol Mechanics

<message/> (carrier for ‘ordinary’ conversational elements)

<presence/> (carrier for presences and availability messages)

<iq/> (carrier for informational and query messages)

With these elements, a client can create messages that accomplish all the design goalsof an instant messaging system and more.

NoteAs you’ll see in later chapters, the Jabber server uses other XML elements for system management, but these

three are the ones clients use most.

You can think of these message types as the basic structure of the carrier of conceptsbetween users, applications, and between Jabber message switches.They are the equiva-lent of spoken language rules whereby words or other elements of sentence structure arecombined to form grammatical sentences.

You may not have realized it (because we haven’t discussed it yet) but all these asyn-chronously generated and arriving XML stanzas with presence, conversation, and queriesdon’t simply appear at the switch completely out of context, as it were.

As shown in Figure 3.1, from a client standpoint, a session with the Jabber switchoccurs in the context of an XML stream.This stream may be viewed as similar to a tele-phone conversation where a “hello” and “goodbye” are the signals for the beginning andend of a conversation. Everything in between is the body of a conversation; whether it’sabout the impending snow storm or a stock transfer is immaterial from the standpoint ofthe switch. It just wants well-formed “sentences” in the stream.The tagging of each indi-vidual message will aid the switch in routing your conversation.

Of course the jabberd is handling many simultaneous streams from potentially manyclients at the same time.As depicted in Figure 3.2, some of your messages are destinedfor other clients, and the switch has to be able to route those on your behalf.

The switch can also, on its own initiative, send messages as a proxy for you. For exam-ple, as Figure 3.2 suggests, when you go offline, a presence message gets sent to otherclients, some of whom are on your roster and who want to alert their users that you areno longer available.Thus, within the possible conversations between a client and a server,some messages are relevant to setting up and maintaining a relationship with the server,and some are concerned with maintaining the conversational context between clients,but all must be expressed in the syntax covered in this chapter and all are containedwithin an XML stream that begins with a <stream> tag and officially ends when theserver gets the matching </stream> tag.This all makes some sense when you rememberthat XML documents can have only a single root tag.Thus everything sent to or via theserver must be contained within the confines of an outermost tag—the <stream></stream> pair.

05 0672325365 CH03 1/21/05 2:23 PM Page 71

Page 89: Jabber Developer’s Handbook [Sams 2004]

72 All About Jabber Clients

Figure 3.1 A conversational stream between a single client and the switch.

�������

��

���

��

���

��������

�� ���� � � �

�� ����� � � �

��� ��������

��� ��������

��� ��������

�����

�������

������

Figure 3.2 Serving multiple clients.

��

���

����������

���������� ���������

�� �

�������

������

05 0672325365 CH03 1/21/05 2:23 PM Page 72

Page 90: Jabber Developer’s Handbook [Sams 2004]

73Protocol Mechanics

To prove that this is so and that we are not just spewing nonsense, write a little shellscript that does the equivalent of a “Hello world” programming example.

In an editor buffer, type this in:

<stream:stream to=’localhost’ xmlns=’jabber:client’

➥ xmlns:stream=’http://etherx.jabber.org/streams’>

If your development Jabber server is somewhere other than localhost, then replacethat bit with the correct information.

Also, type this in on the next line:

</stream:stream>

Next, start a shell to run a Telnet session as shown in Figure 3.3.

Figure 3.3 Telnet “Hello World” example.

Make certain that you turn on local echo as shown in Figure 3.3 so that you can catchthe output returned by the jabberd message switch.

Next, cut the first line from your editor buffer and paste it into the Telnet window, asshown in Figure 3.4.

NoteWe didn’t recommend typing in the initial XML because we can’t type that many characters without an

error; if you’ve a steady hand, however, you could have done so. Also note that there is a time limit imposed

by the server for receiving data and logging in. If you fail to meet that time limit, it boots you off. That’s

another reason not to hand type it.

As shown, the server should return back the string:

<?xml version=’1.0’?><stream:streamxmlns:stream=’http://etherx.jabber.org/streams’

➥ id=’3E453A18’ xmlns=’jabber:client’ from=’localhost’>

To which you reply (either by typing or pasting),

</stream:stream>

05 0672325365 CH03 1/21/05 2:23 PM Page 73

Page 91: Jabber Developer’s Handbook [Sams 2004]

74 All About Jabber Clients

Figure 3.4 Completing “Hello World.”

Essentially, this is the equivalent of ringing the phone, waiting for an answer, then ring-ing off.

The answer you got from the server contained an id field with some hex digits in it.Notice the importance and varying uses of the id as you go through the followingexamples.

To expand the “Hello World” example, let’s exercise one of the three acceptable mes-sage types.This time after opening a session with the <stream> tag shown earlier, let’sask the server what it takes to register a new user by pasting in the following “sentence,”which is an <iq> XML stanza whose type is a ‘get’—we’re asking for information.

<iq id=’AnythingYouWantHere’ type=’get’>

<query xmlns=’jabber:iq:register’/>

</iq>

The server returns an <iq> message with the same ID you used to make the query,whose type is ‘result’:

<iq id=’ AnythingYouWantHere ‘ type=’result’>

<query xmlns=’jabber:iq:register’>

<password/>

<instructions>

Choose a username and password to register with this server.

</instructions>

<name/>

<email/>

<username/>

</query>

</iq>

This is a sentence in Jabber-ese explaining that if you’re going to create a client inthese here parts, you’re going to have to supply a name and password, a nickname (user-name), and an email address. Now you can paste back the following into your Telnetwindow:

05 0672325365 CH03 1/21/05 2:23 PM Page 74

Page 92: Jabber Developer’s Handbook [Sams 2004]

75Protocol Mechanics

<iq id=’WhateverYouWantHere’ type=’set’>

<query xmlns=’jabber:iq:register’>

<username>Praline</username>

<password>Cleese</password>

<name>Mr_Praline</name>

<email>[email protected]</email>

</query>

</iq>

The Jabber server responds back to you using the same ID, and tells you that you aregood to go as a new user:

<iq id=’WhateverYouWantHere’ type=’result’/>

If you were writing a client in a programming language instead of shoving someXML in front of the server’s face with Telnet, you could probably imagine that yourregister user method would jam the outgoing request into a dictionary structurekeyed by id, put yourself into an idle loop, and then whenever the server got around toresponding, you would parse the XML out of the continuously arriving stream, matchup the response and see whether you were successful or not.You can imagine once againthat if this were code, the iq ‘result’ should be interpreted by your client as ‘success’. On the other hand, if you tried a second time to register, the server would slap your virtual wrist by responding as follows:

</iq><iq id=’WhateverYouWantHere’ type=’error’>

<query xmlns=’jabber:iq:register’>

<username>Praline</username>

<password>Cleese</password>

<name> Mr_Praline</name>

<email> [email protected] </email>

</query>

<error code=’409’>Username Not Available</error>

</iq>

This is what you would fervently hope (if you were Mr. Praline) that the Jabber serv-er would do.

Now assume you’ve now logged in successfully as Mr. Praline by opening a Telnetstream, pasting in the opening <stream> tag , and pasting in a login request:

<iq id=’WhateverYouWantHere’ type=’set’>

<query xmlns=’jabber:iq:auth’>

<username>Praline</username>

<password>Cleese</password>

<resource>telnet</resource>

</query>

</iq>

05 0672325365 CH03 1/21/05 2:23 PM Page 75

Page 93: Jabber Developer’s Handbook [Sams 2004]

76 All About Jabber Clients

You got back the hoped for

<iq id=’WhateverYouWantHere’ type=’result’/>

from jabberd. Now say you have some buddies on your roster, which you could havecreated via a set of <iq>-based messages.We’re not going to show that here, as we wantto keep things simple for the sake of illustration. Let’s inject a <presence/> into thestream and see the effects; simply paste in or type:

<presence/>

This forces the server to take a peak at your stored profile (in this case praline.xml)and shove a roster back at you:

<presence from=’jane@localhost/Home’ to=’Praline@localhost’>

<status>available</status>

<priority>0</priority>

<x xmlns=’jabber:x:delay’ from=’jane@localhost/Home’ stamp=’20030208T20:47:44’/>

<x xmlns=’jabber:x:delay’ from=’jane@localhost/Home’ stamp=’20030208T20:47:44’/>

</presence>

<presence from=’dana@localhost/Home’ to=’Praline@localhost’>

<status>available</status>

<priority>0</priority>

<x xmlns=’jabber:x:delay’ from=’dana@localhost/Home’ stamp=’20030208T20:15:29’/>

<x xmlns=’jabber:x:delay’ from=’dana@localhost/Home’ stamp=’20030208T20:15:30’/>

</presence>

Finally, let’s say you wanted to re-enact the infamous Monty Python “Dead Parrot”sketch online. Here, you would use the final type of xml stanza type, <message/>. Onceagain, given that you have opened a Telnet to your jabberd on port 5222, have gottenthe server’s attention with a starting <stream> tag, and are logged in with an <iq> tagshown earlier, you can send a message:

<message from=’Praline@localhost’ to=’dana@localhost’ type=’chat’>

<body>

I wish to complain about this parrot what I purchased not half an

➥ hour ago from this very boutique.

</body>

</message>

Assuming dana is using one of the several real clients and not just typing in XMLstanzas as you have been doing, a chat window pops up on dana’s client (see Figure 3.5),and away you go with the dialogue.

What you actually see in the more primitive Telnet window looks like this, however,with a message ID and thread ID generated by dana’s client (so that it could track subse-quent messages on the same topic—at least as the concept of “topics” is understood bythe communicants). Note too that the apostrophes are escaped as you would expect tosee in a proper XML stream:A client and the server must deal with proper XML con-struction.

05 0672325365 CH03 1/21/05 2:23 PM Page 76

Page 94: Jabber Developer’s Handbook [Sams 2004]

77Protocol Mechanics

Figure 3.5 Using the “message” XML stanza.

<message id=’jcl_52’ to=’Praline@localhost’ type=’chat’

➥ from=’dana@localhost/Home’>

<thread>fd6d59abf7970b853d09580693569290739a80ae</thread>

<body>

Oh yes, the, uh, the Norwegian Blue...What&apos;s,uh...

➥ What&apos;s wrong with it?

</body>

<x xmlns=’jabber:x:event’>

<composing/>

</x>

</message>

If you wanted to respond, you might type into the Telnet stream:

<message id=’’ from=’Praline@localhost’ to=’dana@localhost’ type=’chat’>

<body>

I’ll tell you what’s wrong with it, my lad. ‘E’s dead, that’s what’s

➥ wrong with it!

</body>

</message>

More about the id attribute and the <thread/> tag later.After a bit more of this, you would end the conversation with the server by typing in

the closing </stream:stream> tag. If you were actually typing in the example, youmight have noticed that as soon as you typed in the final > character, the Telnet sessionclosed immediately.The Jabber server reads every character sent to it and uses an event-driven XML parser.As soon as the “hang up” event is fully detected—poof, your sessionis gone.

05 0672325365 CH03 1/21/05 2:23 PM Page 77

Page 95: Jabber Developer’s Handbook [Sams 2004]

78 All About Jabber Clients

Protocol DetailsIn the very crude client constructed earlier we used all the client-to-server elements anddemonstrated a large percentage of the simple things a client can do. Let’s look in greaterdetail at the elements we used so that when you write some more sophisticated clients,you will understand what elements are important with what messages.

The <stream:stream> TagLet’s start with the <stream> tag.As we said earlier, this tag has as potential elementseverything in the conversational stream.Additionally, as you may have noticed, it also hassome attributes.

First of all, the tag identifier has a kind of funny notation, stream:stream.This basi-cally says that this is a stream tag of the type defined and qualified by the namespacestream, whose definition can be found at the URI http://etherx.jabber.org/streams.The little piece of required encoding in the tag that reads xmlns:stream=’http://etherx.jabber.org/streams says precisely that. Notice that it is required tobe in the tag exactly as encoded.

The to Attribute

This attribute specifies the virtual host within the Jabber server. Multiple <host/>entries are allowed in the jabber.xml configuration file—each one is for a separate vir-tual server. It might be that if you’re planning a relatively big farm of jabberd servers, thatyou start out by specifying them on the same physical hosts, then when it runs out ofgas, move each virtual server to a separate physical server in the future.The to attributein a <Stream:stream> tag therefore refers to the virtual server for your particular client.Jabber servers can of course resolve names to DNS addresses for other Jabber servers, soyou can spread servers over hosts as load growth demands.

The <iq> ElementThe login example showed a number of uses for the <iq> tag, and it acted as a containerfor both outgoing requests (from the client to the server) and incoming responses (serverto client). Note that in contrast to certain other protocol structures, the info/querystreams are “in band” with other message types and can occur asynchronously.A niceside effect of completely in-band protocols is that unlike email, where registration is acompletely separate and distinct process, a new user can register with a reachable Jabberserver on the fly.

Other than the asynchronous nature of requests and responses, the mechanism is likeHTTP-GET or -PUT: It enables two entities to make requests and receive responses.

An info/query stanza may possess the following attributes, though not all are required

05 0672325365 CH03 1/21/05 2:23 PM Page 78

Page 96: Jabber Developer’s Handbook [Sams 2004]

79The <iq> Element

or even appropriate in every query conversation, and might reasonably be ignored byeither participant in the conversation:

n to—Specifies the intended recipient of the query stanza.This is optional for mostquery types, as for example when we asked for the registration information fromthe server earlier or even when we sent back our registration information. In thefirst case, the jerry-rigged “client” just wanted some information from the physicaljabberd to which we Telneted. It would have been superfluous to send a specific toentity. In both cases, the client had a really good idea of the identity of the intend-ed recipient. Equally, when the Jabber server responded, it had a good idea of thephysical entity to whom it was responding (and no idea of a jabber-id becauseone didn’t yet exist). It might be, however, that the info/query is not destined forthe jabberd itself but rather for the account setup portion of your news and sportsdelivery component, in which case the switch will definitely want a destinationaddress to which the query stanza should be routed.

n from—Specifies the sender of the query stanza.This is optional for most querytypes, as for example when we asked for the registration information from theserver earlier, or even when we sent back the registration information. Because theuser didn’t exist at either of those points in time, it would have been strange (actu-ally incorrect) for the server to expect a jabber-id.The same holds true of theserver’s responses.Again, it might be the case that the info/query is not destinedfor the jabberd itself, in which case the sender’s JID might be important.Thefrom=’’ attribute is checked by the server to see whether it matches youruser@server. If that part does not match, then it overwrites the from=’’ you sentfrom the Client with the JID that you used to authenticate (that is log in).Thisenables you to control your resource, but you cannot SPAM other users by sayingthat you are someone else.

n id—An optional unique identifier for the purpose of tracking the request-response interaction.The sender of the query stanza could set this attribute, whichmay be returned in any replies.As you may have noticed in the previous examples,you could type in any valid string for an ID.The responder used the same ID to let the sender know to what the response referred.Writing a real client, youwould probably want to use a random number generator to assure uniqueness forquery ID.

n type—This attribute is required in all queries and specifies a distinct point ofinteraction.Transactions and responses generally follow the pattern shown inFigure 3.6.

05 0672325365 CH03 1/21/05 2:23 PM Page 79

Page 97: Jabber Developer’s Handbook [Sams 2004]

80 All About Jabber Clients

Figure 3.6 Common C2S queries and responses.

The type Attribute

If you look back at the example client-to-server (C2S) interactions, you will havenoticed that the dialogue involved in a query is actually pretty simple, and is defined bythe type attribute in the <iq> tag.You can either “get” something (information usually)from a Jabber server, or “set” something at the switch. Normally, a “set” sends stuff to theswitch, which will persist for you in the .xml data file it stores on your behalf.What youget back is either a “result” or an “error.”Any query-capable client you write thereforesimply has to know how to generate queries and tell the difference between good andbad results.

You saw an example of a “get”-type query in the request for registration informationearlier, and a “set”-type query when registration information was supplied to create theuser.You saw “result” and “error” responses when we successfully registered a new userand then tried to register again.

The value of type must be one of the following (anything else is ignored):n get—The stanza is a request for information.The details of the request will be

found in the child element for the tag.The child element must be qualified bynamespace. For example, a request for registration information uses the xml name-space jabber:iq:register.A login request uses jabber:iq:auth.The getqueries shown in the C2S example in Figure 3.6 expect to see one of the resultsreturned.

n set—The stanza contains data intended to provide required data (most likelyspecified in a previous returned result, set new values, or replace existing values.The ‘get’ queries shown in the C2S example in Figure 3.6 expect to see one ofthe results returned.

<iq type=’get’ ><namespace qualified content>

<iq type=‘result‘ ><namespace qualified content >

<iq type=‘set‘ ><namespace qualified content>

<iq type=‘error‘ >

<iq type=‘result‘ >

<iq type=‘error‘ >

ON

Off

Jabbermessageswitch

05 0672325365 CH03 1/21/05 2:23 PM Page 80

Page 98: Jabber Developer’s Handbook [Sams 2004]

81The <iq> Element

n result—The stanza is a response to a successful get or set request.The resultmight have a single XML child element with enumeration and/or clarification ofdata required in a subsequent set query.The single XML child element is a<query> tag pair. In the example that requested registration information, the serverreturned a <query> with several children, with each tag representing a required bitof information or a clarification (the <instructions> element) suitable for displayout to a human reader:

<query xmlns=’jabber:iq:register’>

<password/>

<instructions>

Choose a username and password to register with this server.

</instructions>

<name/>

<email/>

<username/>

</query>

n error—Means that an error has occurred in the processing (or delivery, in the caseof a message being routed to some other entity) of a previously-sent get or set.The returned stanza must include a single <error/> child element, which in turnmust contain a numeric code attribute corresponding to one of the standard errorcodes. It could also contain an optional text string as element text correspondingto a description of the error. In the example where we tried to double register“Mr. Praline,” the server returned an <iq> whose type was error and whose childelement was <error code=’409’>Username Not Available</error>.A com-plete list of error codes is shown in Table 3.1.

Table 3.1 Complete List of Jabber Error Codes

Code Description

302 Redirect

400 Bad Request

401 Unauthorized

402 Payment Required

403 Forbidden

404 Not Found

405 Not Allowed

406 Not Acceptable

407 Registration Required

408 Request Timeout

409 Conflict

500 Internal Server Error

05 0672325365 CH03 1/21/05 2:23 PM Page 81

Page 99: Jabber Developer’s Handbook [Sams 2004]

82 All About Jabber Clients

501 Not Implemented

502 Remote Server Error

503 Service Unavailable

504 Remote Server Timeout 510 Disconnected

TipIf you’re familiar with HTTP, you’ll recognize the numbers in Table 3.1 as the same as HTTP status codes.

Data and Namespaces in <iq> Elements

A key point is that the data content of both requests and responses is qualified by thenamespace declaration of a single direct child element of the <iq> element.That is, toget a definition of what the data mean, you can use the namespace to clarify.The typesof topics that can be included in an <iq> query are qualified by different namespacesthat can be included in the tag.When we logged in with the following:

<iq id=’WhateverYouWantHere’ type=’set’>

<query xmlns=’jabber:iq:auth’>

<username>Praline</username>

<password>Cleese</password>

<resource>telnet</resource>

</query>

</iq>

the meaning of the children tags of the <query> tag are explained in thejabber:iq:auth namespace.

If you are an XML purist we should say that an <iq> element can have only onelegitimate child, the <error/> tag. Otherwise however, whenever there’s a <query> ele-ment, the children are always XML from another namespace.An info/query stanza cancontain any properly-namespaced child element. Even a simple result might containsuch, although it might be unusual. Implement only what is truly necessary in yourclient and forget the rest.A complete list of all the namespaces relevant in Jabber, includ-ing those used in info/queries, is shown in Table 3.2.

Table 3.1 Continued

Code Description

05 0672325365 CH03 1/21/05 2:23 PM Page 82

Page 100: Jabber Developer’s Handbook [Sams 2004]

83The <iq> Element

Table 3.2 Complete List of Jabber Namespaces

Mnemonic (from jabberPy library) Namespace

NS_CLIENT jabber:client

NS_SERVER jabber:server

NS_AUTH jabber:iq:auth

NS_REGISTER jabber:iq:register

NS_ROSTER jabber:iq:roster

NS_OFFLINE jabber:x:offline

NS_AGENT jabber:iq:agent

NS_AGENTS jabber:iq:agents

NS_DELAY jabber:x:delay

NS_VERSION jabber:iq:version

NS_TIME jabber:iq:time

NS_VCARD vcard-temp

NS_PRIVATE jabber:iq:private

NS_SEARCH jabber:iq:search

NS_OOB jabber:iq:oob

NS_XOOB jabber:x:oob

NS_ADMIN jabber:iq:admin

NS_FILTER jabber:iq:filter

NS_AUTH_0K jabber:iq:auth:0k

NS_BROWSE jabber:iq:browse

NS_EVENT jabber:x:event

NS_CONFERENCE jabber:iq:conference

NS_SIGNED jabber:x:signed

NS_ENCRYPTED jabber:x:encrypted

NS_GATEWAY jabber:iq:gateway

NS_LAST jabber:iq:last

NS_ENVELOPE jabber:x:envelope

NS_EXPIRE jabber:x:expire

NS_XHTML http://www.w3.org/1999/xhtml

NS_XDBGINSERT jabber:xdb:ginsert

NS_XDBNSLIST jabber:xdb:nslist

05 0672325365 CH03 1/21/05 2:23 PM Page 83

Page 101: Jabber Developer’s Handbook [Sams 2004]

84 All About Jabber Clients

Jabber PresenceThe <presence/> element has a number of attributes and subelements which we’ll pres-ent in practical usage first, then document more formally.

We presented an instance of this tag earlier in the Telnet session when we suggestedyou try typing in a single-line stanza consisting of only a <presence/> element.Remember that the Jabber server returned a number of <presence> elements inresponse.The primary purpose of this element is the synchronization of presence andavailability among peers who care. By definition, and in every switch implementation,this means each member of a client’s roster. Normally, the switch may cache this list, butbecause the in-memory list is a clone copy, it may refer to the persistent stored versionfor reliability as needed.

Notice that you didn’t need to add to or from’ attributes to your initial presencemessage.The server’s automatic reaction upon receiving an otherwise unqualified <presence/> message is to read your roster from its external persistent store and do twothings. First, it gets the presence of the members of your roster list by sending them pres-ence messages with an attribute of probe. For example:

<presence type=’probe’ to=’jane@localhost’ from=’praline@localhost’/>

If the server can’t connect with the probed user, then it simply drops the packet.Because virtually every GUI client initializes with client’s entire roster shown as“offline,” the portrayal of roster items changes only on arrival of an appropriate <presence/> message.

Second, if the server does connect to a member of your roster, the delivery of a pres-ence-probe message to the client stimulates the client to reply with its complete pres-ence state.What gets returned to you may vary from client to client.The server deliversthat message back to your client, which then updates its GUI.You also did not have tospecify a status element on your initial presence message.The server assumes a state of“available” for you.

This “whatever happens, happens” brand of interaction is illustrated in Figure 3.7.Theserver receives a simple <presence/> message from Client A, reads Client A’s roster andexpands, then sends presences messages to Client B and C. B responds with a <presence/> packet addressed to Client A, which then updates its GUI, but no responseis ever received from Client C. Of course, when Client C finally does come online, itwill send a <presence/> packet to Client A, to which Client A will respond; onresponse, Client C updates its GUI.

You’ve seen how the server mediates on behalf of Client A to multicast its presenceto its roster. So how does Client A get updated with Client C’s presence when Client Ccomes online, or from Client B when its presence changes? The user at Client B might,for example, explicitly set a presence using the client GUI, or Client B might just dis-connect.

05 0672325365 CH03 1/21/05 2:23 PM Page 84

Page 102: Jabber Developer’s Handbook [Sams 2004]

85The <iq> Element

Figure 3.7 Presence probes and response interactions.

In both cases, the server actually does the honors, as one client doesn’t normally sendpresence packets directly to another (although it’s possible to bypass the server’s Jabbersession management—JSM—module if both parties are subscribed to each other’s pres-ence). Because Praline@localhost and jane@localhost are subscribed to one another,Mr. Praline can, after login, simply generate a packet like this:

<presence to=’jane@localhost/Home’ from=’Praline@localhost’>

<status>returning dead parrot</status>

<priority>0</priority>

</presence>

and toss it over the wall to the server. If you have debug turned on at the server (forexample, by running the server from the command line with the –D option), as in

.\jabberd -D -H .

you’ll first see the routing request being generated by the server, then the following:

deliver.c:257 deliver(

to[jane@localhost/Home],from[Praline@localhost],type[2],packet[

ON

Off

<presence/>

<presence/>

<presencetype=“probe”to=“clientB”from=“clientA”/>

<presencetype=“probe”to=“clientC”from=“clientA”/>

GUIUpdatefor ‘B‘

No GUIUpdatefor ‘C‘

client A

clients Bc

Server

STOP

”“ ”

05 0672325365 CH03 1/21/05 2:23 PM Page 85

Page 103: Jabber Developer’s Handbook [Sams 2004]

86 All About Jabber Clients

<presence to=’jane@localhost/Home’ from=’Praline@localhost’>

<status> returning dead parrot </status>

<priority>0</priority>

</presence>

])

The server is generally quite happy to route something for you, so it wraps therequest in a <route> packet and delivers it to [email protected] problem is (as you’llnotice if you run this experiment) that jane@localhost does not respond with its pres-ence.You are short-circuiting the presence and availability mechanism, so this is not awise way to write a client. Rather you should rely upon the subscription mechanisms inthe Jabber server to make the necessary circuits for you.

To allow the server to manage subscriptions for a client pair, the two clients mustmutually agree to subscribe to each other’s presence and availability. One client can’t justprobe another client to whom it’s not subscribed.Try it using the Telnet approach yousaw earlier to generate a presence probe, in this case from Mr. Praline to bill@localhost(these two are not cross-subscribed):

<presence to=’bill@localhost/Home’ from=’Praline@localhost’ type=’probe’/>

The server responds by dropping the packet, as shown in the following server trace:

Sun Feb 23 14:39:59 2003 mod_presence Praline@localhost attempted to probe

➥ by someone not qualified

You can, however, probe someone with whom you have agreed to share presenceinformation. Here, praline@localhost and jane@localhost have both agreed to sharepresence, so Mr. Praline can type in (in a Telnet session):

<presence to=’jane@localhost’ from=’Praline@localhost’ type=’probe’/>

And now the server allows the connection. In response, jane@localhost replies:

<presence from=’jane@localhost/Home’ to=’Praline@localhost’>

<status>available</status>

<priority>0</priority>

<x xmlns=’jabber:x:delay’ from=’jane@localhost/Home’

➥ stamp=’20030223T19:42:13’/>

</presence>

There are some interesting subelements in the response packet.The <status>…bit you have already seen. Generally, clients return either a <show></show> pair or a<status></status> pair (but not often both). Either contains CDATA, which can beany free text, as we document later.There’s also a <priority> tag, which is used to helpdistinguish one presence from another: It may be the case that you are logged in to aparticular Jabber server on two different machines; a higher priority value in the elementsuggests to the server that this is the client to preferentially route packets. Finally, the <xxmlns=’jabber:x:delay’...> bit is a time stamp that a client can use to determine thevintage of information. In this example, the available status was good as of about 7:42p.m. (time-relative to the server).

05 0672325365 CH03 1/21/05 2:23 PM Page 86

Page 104: Jabber Developer’s Handbook [Sams 2004]

87The <iq> Element

Notice that the entire series of conversations takes place asynchronously and relies oneach party in the conversation to do its part.What impresses us as especially clever andin keeping with the overall design philosophy is the asynchronous nature of so manythings in Jabber.There is very little need for the Jabber server to keep huge amounts ofstate information for clients.The switch simply attempts to act as a switch or a router. Ifit’s unable to route a message, then it allows the failure to happen.When clients areoffline, their information is persisted into an XML file, including their roster contents,and (optionally) their preferences.When a client returns online, the Jabber server reads inthe persisted XML file, whose format we covered in Chapter 2,“Installing andConfiguring Jabber Software,” using its XDB capability, and acts upon what it finds (par-ticularly roster information) there.The switch follows to the rule,“Do only what’srequired and no more.” Strict adherence to this rule makes for a lean and mean switch,both functionally and footprint-wise.

Notice too the division of labor among parties in Jabber.The server merely deliversthe presence-probe to the clients on your roster.What they choose to do in response tothe probe is not the server’s concern. Clients most often respond by sending a presencepacket—indeed that’s the expected behavior.When a client sends a responsorial presencepacket, the server simply delivers it to you without having to remember that this packetis somehow connected with something that it sent to you previously.

NoteWe aren’t going to cover the intricacies of writing a complete server in this book, but understanding these

concepts will help you should you decide to use the open source Jabber server as a way of getting started

toward building your own or making your own contribution to the open source implementation.

Presence AttributesIn the primitive example at the beginning of the chapter, the presence message wasunmodified by additional attributes, but there are a few worthy of mention that youneed to put into your programming toolkit. Let’s formally talk about them now.

The <presence> ElementIn addition to being used as a carrier for clients’ presence and availability, the <presence/> element is also used by the server as a management tool for connectedclients.The <presence/> element is also used to negotiate and manage subscriptions tothe presence of other entities.You have seen some practical examples of using presencealready, but to formalize your understanding, you should look at some of the attributesof the <presence/> tag itself.

05 0672325365 CH03 1/21/05 2:23 PM Page 87

Page 105: Jabber Developer’s Handbook [Sams 2004]

88 All About Jabber Clients

The <presence/> Attributes

A presence element can have several attributes, not all of which are appropriate to agiven purpose.

n to—Specifies the intended recipient of the presence stanza (if any). Often, thisattribute is filled in by the Jabber server rather than specified by a client.A <presence/> message with no other qualification attributes is normally issuedwhen a client session connects.This is expanded by the Jabber server into messagesfor each of the client’s roster members.After it is expanded by the server, a <presence> message contains both to and from attributes. Examples were shownearlier in the section “Protocol Mechanics.”

n from—Specifies the sender of the presence stanza. Normally this is reflected backto a sender as a part of a response to a presence multi-cast managed by the Jabberserver’s session manager module.Again, the from attribute is verified against yourJID and overwritten if the user@server does not match the JID with which youlogged in. Usually it’s safest to not send it.

n id—A unique identifier for the purpose of tracking presence.The sender of thepresence stanza sets this attribute, which may be returned in any replies.Althoughit is permissible to include an id attribute, it has no value for presence manage-ment.

n type—Describes the availability state, subscription request, presence request, orerror.When there is no type attribute there is an implied value of available.When specified, type should have one of the values shown in Table 3.3.

Table 3.3 Complete List of Jabber <presence/> Type Attributes

Value Significance

available This is the default availability value.To signify a presence of “available,” don’t seta type attribute at all in the outgoing packet.

unavailable The sending client (identified in the from attribute) is no longer available forcommunication.This likely will have been sent by the Jabber server (on a client’sbehalf) when a client disconnect was detected.

probe This attribute is normally issued by the Jabber server on behalf of a client andaddressed to the clients on the originating client’s roster.The normal form is:

<presence type=’probe’ to=’user@server’ from=’user@server’/>

A resource may be specified too (as in to=’user@server/resource’, and isnot ignored if present.

A client may do its own probes (only to clients with which it has mutuallyagreed to share presence information—avoids wanton misuse of the capability).The <presence> message must be fully qualified with from, to, andtype=probe attributes.

subscribe The sending client (identified in the from attribute) wishes to subscribe to therecipient’s presence.The recipient is identified in the to attribute.

05 0672325365 CH03 1/21/05 2:23 PM Page 88

Page 106: Jabber Developer’s Handbook [Sams 2004]

89The <iq> Element

unsubscribe The sending client (identified in the from attribute) wishes to unsubscribe tothe recipient’s presence.The recipient is identified in the to attribute.

subscribed The sending client (identified in the from attribute) agrees to allow the recipi-ent (identified in the to attribute) to access its presence information.The recipi-ent originated the conversational exchange via a presence message whose typewas subscribe.

unsubscribed The sending client (identified in the from attribute) acknowledges an unsubscribe request from the recipient (identified in the to attribute) to accessits presence information.The recipient originated the conversational exchange viaa presence message whose type was unsubscribe.

error An error occurred in the server’s processing or delivery of the presence stanza.

The <presence> Subelements

A presence element can have four distinct stanzas as subelements, not all of which areneeded in a particular situation.

n <show/>—This stanza describes the availability of a client in terms of specific val-ues. Other values are ignored.A <show> element typically qualifies a <presence>element whose type attribute is available.A presence element usually contains a<show> child element or a <status> child element but not both, as shown inTable 3.4.

Table 3.4 Complete List of Jabber “Standard” Presence Codes

Value Significance

away The client specified in the from attribute is temporarily away from the keyboard.

chat The client specified in the from attribute is available for “chat.” Generally superflu-ous, as an available client is assumed to be available for chat.

dnd The client specified in the from attribute has marked its availability as “do not disturb” (dnd = “Do Not Disturb”).

normal The client is available. Once again, generally superfluous.xa The client specified in the from attribute is away from the keyboard for an extend-

ed period of time. (xa == “eXtended Away”).

n <status/>—An optional natural-language description of availability status.Normally a client issues this as an explicit expression of availability:“I am in ameeting,”“I am writing,” and so on. It is used in conjunction with the show ele-ment to provide a detailed description of an availability state (for example,“In ameeting”).

Table 3.3 Continued

Value Significance

05 0672325365 CH03 1/21/05 2:23 PM Page 89

Page 107: Jabber Developer’s Handbook [Sams 2004]

90 All About Jabber Clients

n <priority/>—The primary purpose of the priority stanza is to suggest to theserver which instance of a particular jabberId@jabberServer is the “best” or “firstamongst equals” to which a message ought to be routed.Why is this important?Well, suppose you’re logged in at your work desk as [email protected], but likemany of us you are managing by walking around and you are also logged in onyour PDA as [email protected]. Now suppose [email protected] wants to sendyou a message.To which presence does the server choose to route a message? Ifthe message is fully qualified with jabberID@jabberServer/Resource, then thismight certainly be used to disambiguate. But what if Sam-I-Am is not too brilliantand just addresses a message without a resource? This is where priority comes intoplay. Clients can set a priority (a non-negative integer) to represent the prioritylevel of the connected resource, with zero as the lowest priority.The higher thenumber, the more preferred the resource is. Note that although the specificationallows a negative priority number to mean that the sender should not be used fordirect or immediate contact, not all clients or servers implement this control.Almost all known GUI clients have a control panel to allow setting this value asshown in Figure 3.8.

Figure 3.8 Setting presence priority via GUI.

n <error/>—If the server routes a <presence/> message that includes atype=”error” attribute, the presence stanza must include an <error/> child ele-ment, which in turn should contain a numeric code attribute corresponding toone of the standard error codes listed in Table 3.1 and may also contain PCDATAcorresponding to a natural-language description of the error.

05 0672325365 CH03 1/21/05 2:23 PM Page 90

Page 108: Jabber Developer’s Handbook [Sams 2004]

91The <iq> Element

Finally, note that a <presence/> message might also contain a namespace-qualifiedsubelement.You as a developer of a client can choose to either ignore these or parse theones important to your application.

The <message> ElementThe message element is the unglamourous “foot soldier” of all the message types. Itsattributes and child elements are relatively easy to understand.A minimal message mustcontain the address elements as attributes on the message tag itself.The to attribute mustappear, and a <body> subelement:

<message id=’’ to=’jane@localhost’ from=’dana@localhost/Home’>

<body>hi</body>

</message>

Again, in a Client, the from is verified against your JID and overwritten if theuser@server does not match the JID with which you logged in. It is not incorrect toset and send it, but it is usually best to not send it at all.

Assuming that the two clients were connected to localhost, jane@localhost wouldreceive a message from dana@localhost, and this would result in a message rather than achat session because no type field has been specified.

If a chat stream had been opened by one of the end-points specifying type=chat asan attribute on the message tag, then a chat window is opened and the message becomesslightly more complex (see Figure 3.9).

Figure 3.9 Typical message content in a GUI.

In the first message exchange, the chat session is initiated by setting the message type tochat (type=’chat’).A thread is established (the thread subelement) to uniquely tag thesession between the two clients. More about the <thread/> subelement briefly.

<message id=’jcl_7’ to=’jane@localhost’ type=’chat’ from=’dana@localhost/Home’>

<thread>9164572e818c2845f73b442cbc62e1ccf6c3cb48</thread>

05 0672325365 CH03 1/21/05 2:23 PM Page 91

Page 109: Jabber Developer’s Handbook [Sams 2004]

92 All About Jabber Clients

<body>how&apos;s the sailing today?</body>

<x xmlns=’jabber:x:event’>

<composing/>

</x>

</message>

For completeness’ sake, let’s identify the attributes and subelements of <message/>formally.

The <message/> Attributes

A message element can have several attributes, not all of which are appropriate to a givenpurpose.

n to—Specifies the message’s intended recipient.This attribute is populated by theclient initiating the dialogue. In general the JabberID, which is the value on theright side of the equal sign token for the to attribute, is of the form user@server/resource.The form user@host is often seen too, though, and either resolves touser@host/resource by the Jabber server if the recipient is online, or is directedto offline storage if the user is not online.This attribute is required for all <message/> elements.We’ve shown several examples already in this chapter.

n from—Specifies the sender of the message stanza. Normally this is reflected backto a sender as a part of a response to a presence multi-cast managed by the Jabberserver’s session manager module. In general the Jabber ID for the from attributemust be of the form user@server/resource; however user@server is alsoacceptable. It may happen that the server might modify or replace the value of thisattribute (for example to prevent certain kinds of spoofing), but it’s normal practicefor you to insert this element in your own client code because it is required for all<message/> elements.

n id—A unique identifier whose value is a string generated by the Jabber client orclient library, and is used by the client to identify the message for tracking purpos-es (most commonly to correlate messages sent to a group chat room with messagesreceived from the room, or to associate a previously sent message with any errors itmight generate).The id attribute is optional.

n type—Qualifies a message and gives “hints” to a client about what sort of visualinterface might be appropriate to display the message.Although the method ofportraying the message view is entirely left up to the client, almost all GUI clientsprovide a different view based on message type. Most interfaces provide an ongo-ing thread view for chat and groupchat interchanges, and a more “email-like” viewfor the default message type. For contrast, though, see the unique Lluna client (see“Resources” in Appendix C; see http://www.jabber.org/user/clientlist.phpfor a comprehensive client list), which uses animated avatars on a Web page.Table3.5 lists the valid values of the type attribute.

05 0672325365 CH03 1/21/05 2:23 PM Page 92

Page 110: Jabber Developer’s Handbook [Sams 2004]

93The <iq> Element

Table 3.5 Complete List of Jabber <message/> Type Attributes

Value Significance

no value or not present When nothing is specified, this is a hint that suggests that thereceiving client should consider the message as possibly having a“subject” and message “body.” Often, clients present this in a sepa-rate non-modal window.

chat This is a hint that suggests that the receiving client should displaythe message in a typical line-by-line, rolling log chat interface(although the exact interface may be client-specific).

groupchat This is a hint that suggests that the receiving client should displaythe message in a “chatroom”-style interface, most famously seen inAOL/AIM or IRC implementations.

headline Enables you to encapsulate a news article with a title and body subelement. Uses the <x xmlns=’jabber:x:oob’> namespace tag.

error Generally this indicates a response message from the Jabber server. Ifthe value of the type attribute is error, the message should con-tain an <error/> subelement.This subelement is more fullydescribed later in this chapter.

The <body/> SubelementThe <body/> stanza is a child element of <message/> that carries the message’s content.It must not carry any tag attributes.As seen in the earlier “Dead Parrot Sketch” example,notice there’s something interesting going on in the actual message data itself containedin the <body></body> tag pair—more about that briefly. Finally, there is an inclusion ofXML content from an external namespace (jabber:x:event).This namespace is a stan-dard Jabber namespace that is used to request and respond to message events relating tothe delivery, display, and composition of messages.We showed the Jabber namespacemore fully in Table 3.2 (earlier), but basically it is one of four events, listed in Table 3.6,that either a client issues when sending a message or that the server issues on a client’sbehalf.

Table 3.6 jabber:x:event Attachments to a Message

Value Significance

Offline Indicates that the server has stored the message offline because the intendedrecipient is not available.This event is to be raised by the Jabber server.

Delivered Indicates that the message has been delivered to the recipient.This signifies thatthe message has reached the Jabber client, but does not necessarily mean that themessage has been displayed.This event is to be raised by the Jabber client.

Displayed After the Jabber client has received the message, it may be displayed to the user.This event indicates that the message has been displayed, and is to be raised bythe Jabber client. Even if a message is displayed multiple times, this event shouldbe raised only once.

05 0672325365 CH03 1/21/05 2:23 PM Page 93

Page 111: Jabber Developer’s Handbook [Sams 2004]

94 All About Jabber Clients

Composing In threaded chat conversations, this indicates that the recipient is composing areply to a message that was just sent.The event is to be raised by the Jabberclient.A Jabber client is allowed to raise this event multiple times in response tothe same request, providing that a specific sequence is followed.

Therefore, in your code, whenever you add a jabber:x:event extension to a <message/> element, your code as the message sender can track stages in the delivery ofthat element to its recipient.As shown in the “Dead Parrot Sketch” example, it merelysays that the recipient can potentially expect more data later.

XML CDATA in the <body/> Subelement

In Figure 3.9, when jane@localhost replies to dana@localhost, she says:“I’ll send youthe Annapolis <weather-report-url>.” However, when the client generates the mes-sage, this is what the message body looks like in raw form:

<message id=”jcl_8” to=”dana@localhost/Home” type=”chat”>

<thread>9164572e818c2845f73b442cbc62e1ccf6c3cb48</thread>

<body>I &apos;ll send you the Annapolis &lt;weather-report-url&gt;</body>

<x xmlns=”jabber:x:event”>

<composing/></x>

</message>

What’s going on here? Well, remember that Jabber is XML-based messaging.Therefore the CDATA carried in the message body must properly escape the apostro-phe, the quote symbol, the ampersand, the less-than symbol, and the greater-than sym-bol.Thus your client code can’t just send an unexamined message body to the peer onthe other side of the conversation.Additionally, the receiving peer must decode theescaped XML as well. Just remember that everywhere in your code that you prepare textfor transmission, you need to translate occurrences of special XML characters(&, <, >,and so on) appropriately, and whenever you want to get the textual data from an XMLnode (for example, the <body/> of a message), you do the opposite.

Fortunately, if you’re creating an application atop a client library, the library creatoralready thought this through for us. For example, in the Python JabberPy package (see“Resources” in Appendix C), the XMLStream handler maintains two simple methods:

def XMLescape(txt):

“Escape XML entities”

txt = replace(txt, “&”, “&amp;”)

txt = replace(txt, “<”, “&lt;”)

txt = replace(txt, “>”, “&gt;”)

return txt

def XMLunescape(txt):

“Unescape XML entities”

Table 3.6 Continued

Value Significance

05 0672325365 CH03 1/21/05 2:23 PM Page 94

Page 112: Jabber Developer’s Handbook [Sams 2004]

95The <iq> Element

txt = replace(txt, “&amp;”, “&”)

txt = replace(txt, “&lt;”, “<”)

txt = replace(txt, “&gt;”, “>”)

return txt

If you’re writing a client from scratch in, say, Python or Java, you should do likewise.Here’s another snippet from JabberPy that shows transformation of XML to a string rep-resentation suitable for display (for example, in a debug window). Notice that it recurseson itself (line 16), works across namespaces (lines 4-7), and constructs a string representa-tion with escapes properly handled (lines 10, 15, and 18).

01: def _xmlnode2str(self, parent=None):

02: “””Returns an xml ( string ) representation of the node

03: and it children”””

04: s = “<” + self.name

05: if self.namespace:

06: if parent and parent.namespace != self.namespace:

07: s = s + “ xmlns = ‘%s’ “ % self.namespace

08: for key in self.attrs.keys():

09: val = str(self.attrs[key])

10: s = s + “ %s=’%s’” % ( key, XMLescape(val) )

11: s = s + “>”

12: cnt = 0

13: if self.kids != None:

14: for a in self.kids:

15: if (len(self.data)-1) >= cnt: s = s + XMLescape(self.data[cnt])

16: s = s + a._xmlnode2str(parent=self)

17: cnt=cnt+1

18: if (len(self.data)-1) >= cnt: s = s + XMLescape(self.data[cnt])

19: s = s + “</” + self.name + “>”

20: return s

Additional <message/> SubelementsIn addition to the <body/> subelement, a message stanza may contain zero or one ofeach of the following stanzas as child elements (which may not contain mixed content):

NoteIn actual fact, a <message/> is not even required to have a <body/> subelement; The XML specifica-

tion requires zero or one <body/> subelement. However, it seems to us that it wouldn’t be much of a

message without saying something—a bit like a sentence consisting of a single period.

n <subject/>—The message’s subject.The <subject/> tag cannot contain attributes.

<message id=’’ to=’bill@localhost’ from=’dana@localhost/Exodus’>

<subject>Chapter 10</subject>

<body>

05 0672325365 CH03 1/21/05 2:23 PM Page 95

Page 113: Jabber Developer’s Handbook [Sams 2004]

96 All About Jabber Clients

How is chapter 10 coming along?

</body>

</message>

Normally if the message’s type attribute is set to chat, the client does not providea capability to set a subject, although nothing in the protocol forbids this. It’s sim-ply an idiosyncracy of chatting versus messaging.

n <thread/>—A random string that is generated by the sender and that may becopied back in replies, although nothing in the protocol absolutely demands that itmust be. It is used for tracking a conversation thread.The <thread/> element cannot contain attributes.

<message id=’jcl_7’ to=’dana@localhost’ type=’chat’from=’bill@localhost/Exodus’>

<thread>0ef291b610e9ec253f0970ebdb0477e1574a4e32</thread>

<body>The chapter is going fine</body>

<x xmlns=’jabber:x:event’>

<composing/>

</x>

</message>

In customary client usage, whenever a GUI chat window is closed by either client,a new chat message will generate a new thread value.

n <error/>—If the message contains an attribute where type=”error”, the <message/> stanza must include an <error/> child, which in turn must have astandard code attribute corresponding to one of the standard error codes shownearlier, and could also contain PCDATA corresponding to a natural-languagedescription of the error.

This is demonstrated in the following example, in which client code mistakenlysends a message to a non-existent user (dana@localhost sends a message towilliam@localhost instead of the correct bill@localhost):

<message to=”dana@localhost” from=william@localhost type=”error”>

<body>hi</body>

<error code=”404”>Not Found</error>

</message>

n <x/> (external) stanzas—A message may also contain any properly-namespacedchild element (other than the common PCDATA elements, <stream:stream/>elements, or children of <stream:stream/> elements).This is one way of extend-ing the <message/> element in an arbitrary way.An <x/> stanza can also be usedas a protocol element to send commands from server to client or from one clientto another. Each time the element is used, a relevant XML namespace must bespecified.A single message may contain multiple instances of the <x/> subelement.The namespaces most often occurring in a message with an <x/> subelement arelisted in Table 3.7.

05 0672325365 CH03 1/21/05 2:23 PM Page 96

Page 114: Jabber Developer’s Handbook [Sams 2004]

97The <iq> Element

NoteThe <x/> tag is not just a child of <message/>. It is commonly used here, but current work and research

is also moving to embedding it in <iq/>, <query/>, and often <presence/> tags.

Table 3.7 Common Extended Namespaces in a <message/>

Namespace Use

jabber:x:autoupdate Possible use: Enable arbitrary client-to-application queries aboutany software updates or version changes available.

jabber:x:delay Used to provide timestamp information about messages and pres-ence information. Conveyed on presence responses and stored forlater delivery when sent to an offline Jabber client. In the lattercase, as the client comes back online, this namespace includes infor-mation that enables the Jabber client to display the time when thepacket was originally sent.

Message example:<message type=’groupchat’ from=’bill@localhost’>

<body>Looks like a good day for sailing!</body>

<x xmlns=’jabber:x:delay’

stamp=’20030313T15:35:43’>Cached</x>

</message>

Presence example:<presence from=’dana@localhost/Exodus’

to=’bill@localhost’>

<status>available</status>

<priority>0</priority>

<x xmlns=’jabber:x:delay’

from=’dana@localhost/Exodus’

stamp=’20030314T12:22:14’/>

</presence>

jabber:x:encrypted Supports exchange of messages encrypted using the public keyinfrastructure (normally implemented using PGP by the client).Arelated namespace, jabber:x:signed, is used to support signedmessages.

Message example:<message from=’dana@localhost/Exodus’

to=’bill@localhost/Exodus’>

<body>This Message is Encrypted</body>

<x xmlns=’jabber:x:encrypted’>

… PGP encrypted message…

</x>

</message>

05 0672325365 CH03 1/21/05 2:23 PM Page 97

Page 115: Jabber Developer’s Handbook [Sams 2004]

98 All About Jabber Clients

jabber:x:oob Indicates “out of band” data. Enables clients to exchange a standardURI with a description for the purpose of file transfers. URIsexchanged using jabber:x:oob can be included with any mes-sage (inside an <x/> subelement) and act as an attachment in thesense familiar from email. Multiple attachments can be included inone message.

Message example:<message from=’dana@localhost/Exodus’

to=’bill@localhost/Exodus’

type=’chat’>

<body>URL Attached.</body>

<x xmlns=”jabber:x:oob”>

<url>

http://java.sun.com/javaone

</url>

<desc>

JavaOne Site

</desc>

</x>

</message>

Note:A client may choose to ignore OOB data, in which casenothing is seen at the client.

jabber:x:roster This namespace allows a user to include roster items within a mes-sage, thus making it easy to send contact lists from one user toanother. Each roster item is contained in an <item/> subelementwithin an <x/> element.

Message example:<message to=”bill@localhost”

type=’chat’

from=’dana@localhost’>

<body>My contacts!</body>

<x xmlns=”jabber:x:roster”>

<item jid=”jane@localhost”

name=”jane”>

<group>Exodus</group>

</item>

<item jid=”Praline@localhost”

name=”Mr Praline”>

<group>Friends</group>

</item>

</x>

</message>

Table 3.7 Continued

Namespace Use

05 0672325365 CH03 1/21/05 2:23 PM Page 98

Page 116: Jabber Developer’s Handbook [Sams 2004]

99The <iq> Element

Using <message/> to Convey Arbitrary DataIn its most familiar usage, Jabber facilitates the movement of short bits of text from oneclient to the next.What those textual bits mean in any true sense of the word is up tothe interpretation of the humans or systems at the end points of the communicationwith mediation from the client software. From this perspective the Jabber server is“dumb” and clients may grow arbitrarily “smart.”

From a practical standpoint, all that client end points have to ensure is that the XMLstream they transmit conforms syntactically to the standards described thus far in thebook. Message bodies can therefore contain pretty much any data the communicantswant to put there.The only proviso is that the message body be contained within a<body/> tag and look like text.Therefore, you could, for example, embed Base64 datarepresenting anything in a message body and have the client programs code and decodethe data.

To illustrate this point let’s look at an application that is an extreme use to suggestthat the outer limits of intended usage are indeed pretty “outer.” For this exercise we aregoing to create an application that shares pictures between roster members.The applica-tion works like this:Anytime a client endpoint opens and displays a picture (GIF, JPEG,and so on) from the file system, that picture is transported to the endpoint’s roster mem-bers. For this exercise, we will assume all roster members are using the same client.Theclient code shown is a purpose-built client that handles only the intended functionalitywe have described. It is not a general Jabber textual chat client; as the code will show, itsupports only examination of one’s roster, encoding of the binary data in an image, andtransmission to the roster concurrent with file opening and display.

We have a number of times said that a client need only support as much of the Jabberprotocol elements as suit its needs, and this application which mixes in Jabber transportwith a picture viewer application illustrates that point well.As a side effect, this exampledemonstrates that it’s relatively easy to add Jabber functionality to general applications.One of our disappointments with many technical books is that code examples are oftencontrived and don’t resemble the approach that you would actually take in coding a realapplication. Here we’ve taken a real Ruby language GUI application, stripped it down abit so that it’s easier to present, and then added a few simple lines that turn it into aJabber client.

NoteThis example comes from the FOX Ruby site (http://fxruby.sourceforge.net) .FXRuby is a Ruby

language extension module that provides an interface to the FOX GUI library. In imageviewer.rb you

can see an application that is a model for a typical full-featured GUI application, with a menu bar, toolbar,

and so forth.

Consider a group of clients whose relationship may be discerned from Figure 3.10.

05 0672325365 CH03 1/21/05 2:23 PM Page 99

Page 117: Jabber Developer’s Handbook [Sams 2004]

100 All About Jabber Clients

Figure 3.10 A group of clients.

Notice that peer1’s roster consists of peer2 and peer3. Peer2’s roster consists of peer1.Peer3’s roster consists of peer1 also.

In this application, any graphic file that peer1 opens and displays will be transmittedto both peer2 and peer3, replacing whatever was on their local canvases.Any graphic filethat peer2 opens and displays will be transmited to peer1, replacing whatever was on itslocal canvas. Likewise with peer3.Therefore, Figure 3.11 must have been generated bypeer1 opening and displaying a picture of a rather wily looking coyote.

How does this work? First, Listing 3.1 shows the Ruby code in its entirety, thendeconstructs it.To run this code experimentally, you should install a current release ofRuby on your system, then add the jabber4r library.You also need to download the FOXRuby library. Create some clients sharing rosters, as we showed in the previous figures.From the command line, launch an instance via

ruby bitmapper.rb ‘account@host/resource’ ‘password’

ON

Off

Jabbermessageswitch

Exodus Tools Help

bitmapperpeer2peer3

Available

Exodus Tools Help

Available

++++ ++++

Exodus Tools Help

bitmapperpeer1

Available

++++

bitmapperpeer1

Exodus - peer1... X Exodus - peer3... X

Exodus - peer2... X-

--

05 0672325365 CH03 1/21/05 2:23 PM Page 100

Page 118: Jabber Developer’s Handbook [Sams 2004]

101The <iq> Element

Figure 3.11 Binary messaging application in action.

Listing 3.1 Bitmapper.rb

1:#!/usr/bin/env ruby

2:

3:

4:

5:require ‘jabber4r/jabber4r’

6:

7:(puts “bitmapper.rb ‘account@host/resource’ ‘password’”)

➥ & exit if ARGV.size < 2

8:

9:

10:require ‘fox’

11:

12:include Fox

13:

��

���

������

�������

����

05 0672325365 CH03 1/21/05 2:23 PM Page 101

Page 119: Jabber Developer’s Handbook [Sams 2004]

102 All About Jabber Clients

14:class ImageWindow < FXMainWindow

15:

16: attr_reader :jabber_session

17:

18: include Responder

19:

20: def initialize(app)

21: # Invoke base class initialize first

22: super(app, “Jabber Image Sender (#{ARGV[0]}): - untitled”,

➥ nil, nil, DECOR_ALL,

23: 0, 0, 850, 600, 0, 0)

24:

25: # Make some icons

26: uplevelicon = getIcon(“tbuplevel.png”)

27:

28: # Status bar

29: statusbar = FXStatusbar.new(self,

30: LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|STATUSBAR_WITH_DRAGCORNER)

31:

32: # Splitter

33: splitter = FXSplitter.new(self, (LAYOUT_SIDE_TOP|LAYOUT_FILL_X|

34: LAYOUT_FILL_Y|

➥ SPLITTER_TRACKING|SPLITTER_VERTICAL|SPLITTER_REVERSED))

35:

36: # Sunken border for image widget

37: imagebox = FXHorizontalFrame.new(splitter,

38: FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_Y,

39: 0, 0, 0, 0, 0, 0, 0, 0)

40:

41: # Make image widget

42: @imageview = FXImageView.new(imagebox, nil, nil, 0,

43: LAYOUT_FILL_X|LAYOUT_FILL_Y)

44:

45: # Sunken border for file list

46: @filebox = FXHorizontalFrame.new(splitter, LAYOUT_FILL_X|LAYOUT_FILL_Y,

47: 0, 0, 0, 0, 0, 0, 0, 0)

48:

49: # Make file list

50: fileframe = FXHorizontalFrame.new(@filebox,

51: FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_Y,

52: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

53: @filelist = FXFileList.new(fileframe, nil, 0,

54: LAYOUT_FILL_X|LAYOUT_FILL_Y|ICONLIST_MINI_ICONS|ICONLIST_AUTOSIZE)

55: @filelist.connect(SEL_DOUBLECLICKED, method(:onCmdFileList))

56: FXButton.new(@filebox, “\tUp one level\tGo up to higher directory.”,

Listing 3.1 Continued

05 0672325365 CH03 1/21/05 2:23 PM Page 102

Page 120: Jabber Developer’s Handbook [Sams 2004]

103The <iq> Element

57: uplevelicon, @filelist, FXFileList::ID_DIRECTORY_UP,

58: BUTTON_TOOLBAR|FRAME_RAISED|LAYOUT_FILL_Y)

59:

60: # Initialize file name and pattern for file dialog

61: @filename = “untitled”

62: @preferredFileFilter = 0

63: end

64:

65: # Convenience function to construct a PNG icon

66: def getIcon(filename)

67: FXPNGIcon.new(getApp(), File.open(filename, “rb”).read)

68: end

69:

70: def hasExtension(filename, ext)

71: File.basename(filename, ext) != File.basename(filename)

72: end

73:

74: def loadImage(file)

75: file = file.gsub( /\\/ , “/” ) # if you’re on WIN32..

76: sendJabberImage(file)

77: updateImage(file)

78: end

79:

80: # Load the named image file

81: def updateImage(file)

82: img = nil

83: if hasExtension(file, “.gif”)

84: img = FXGIFImage.new(getApp(), nil, IMAGE_KEEP|IMAGE_SHMI|IMAGE_SHMP)

85: elsif hasExtension(file, “.bmp”)

86: img = FXBMPImage.new(getApp(), nil, IMAGE_KEEP|IMAGE_SHMI|IMAGE_SHMP)

87: elsif hasExtension(file, “.xpm”)

88: img = FXXPMImage.new(getApp(), nil, IMAGE_KEEP|IMAGE_SHMI|IMAGE_SHMP)

89: elsif hasExtension(file, “.png”)

90: img = FXPNGImage.new(getApp(), nil, IMAGE_KEEP|IMAGE_SHMI|IMAGE_SHMP)

91: elsif hasExtension(file, “.jpg”)

92: img = FXJPGImage.new(getApp(), nil, IMAGE_KEEP|IMAGE_SHMI|IMAGE_SHMP)

93: elsif hasExtension(file, “.pcx”)

94: img = FXPCXImage.new(getApp(), nil, IMAGE_KEEP|IMAGE_SHMI|IMAGE_SHMP)

95: elsif hasExtension(file, “.tif”)

96: img = FXTIFImage.new(getApp(), nil, IMAGE_KEEP|IMAGE_SHMI|IMAGE_SHMP)

97: elsif hasExtension(file, “.tga”)

98: img = FXTGAImage.new(getApp(), nil, IMAGE_KEEP|IMAGE_SHMI|IMAGE_SHMP)

99: elsif hasExtension(file, “.ico”)

100: img = FXICOImage.new(getApp(), nil, IMAGE_KEEP|IMAGE_SHMI|IMAGE_SHMP)

101: end

Listing 3.1 Continued

05 0672325365 CH03 1/21/05 2:23 PM Page 103

Page 121: Jabber Developer’s Handbook [Sams 2004]

104 All About Jabber Clients

102:

103: # Perhaps failed?

104: if !img

105: FXMessageBox.error(self, MBOX_OK, “Error loading image”,

106: “Unsupported image type: #{file}”)

107: return

108: end

109:

110: # Load it...

111: getApp().beginWaitCursor do

112: FXFileStream.open(file, FXStreamLoad) {

➥ |stream| img.loadPixels(stream) }

113: img.create

114: @imageview.image = img

115: end

116: end

117:

118: # Quit the application

119: def onCmdQuit(sender, sel, ptr)

120: # Quit

121: getApp().exit(0)

122: end

123:

124: # Command message from the file list

125: def onCmdFileList(sender, sel, index)

126: if index >= 0

127: if @filelist.isItemDirectory(index)

128: @filelist.directory = @filelist.getItemPathname(index)

129: elsif @filelist.isItemFile(index)

130: @filename = @filelist.getItemPathname(index)

131: loadImage(@filename)

132: end

133: end

134: return 1

135: end

136: # Create and show window

137: def create

138: puts “creating app”

139: dir = “.”

140: @filebox.height = 100

141: super # i.e. FXMainWindow::create()

142: show(PLACEMENT_SCREEN)

143: end

144:

145:

Listing 3.1 Continued

05 0672325365 CH03 1/21/05 2:23 PM Page 104

Page 122: Jabber Developer’s Handbook [Sams 2004]

105The <iq> Element

146:

147: def connectToJabber(account, account_password)

148: begin

149: @jabber_session = Jabber::Session.bind_

➥ digest(“#{account}”, “#{account_password}”)

150: @jabber_session.add_message_listener do |message|

151: File.open(“TMP”+message.subject, “wb”) do |file|

152: file.write message.body.unpack(“m”)[0]

153: file.flush

154: end

155: updateImage(“TMP”+message.subject)

156: File.delete(“TMP”+message.subject)

157:

158: end

159: rescue

160: puts “Account does not exist: #{account} - #{account_password}”

161: puts $!

162: puts $!.backtrace

163: exit

164: end

165: end

166:

167: def sendJabberImage(imagefile)

168: puts imagefile

169: @jabber_session.roster.each_item do |item|

170: puts item

171: item.each_resource do |resource|

172: if resource.name == “bitmapper”

173: data = nil

174: File.open(imagefile, “rb”) {|file| data = file.read}

175: data = [data].pack(“m”)

176: @jabber_session.new_chat_message(item.jid.to_s+”/bitmapper”)

.set_subject(File.basename(imagefile)).set_body(data).send

177: end

178: end

179: end

180: end

181:

182: def disconnectFromJabber

183: @jabber_session.close if @jabber_session

184: end

185:end

186:

187:if $0==__FILE__

188: # Make application

Listing 3.1 Continued

05 0672325365 CH03 1/21/05 2:23 PM Page 105

Page 123: Jabber Developer’s Handbook [Sams 2004]

106 All About Jabber Clients

189: application = FXApp.new(“ImageViewer”, “FoxTest”)

190: # Make window

191: window = ImageWindow.new(application)

192:

193: # Handle interrupts to terminate program gracefully

194: application.addSignal(“SIGINT”, window.method(:onCmdQuit))

195:

196: # Create it

197: application.create

198:

199: # Connect to Jabber

200: window.connectToJabber(*ARGV)

201:

202: # Run

203: application.run

204:

205: # Shutdown Jabber connection

206: window.disconnectFromJabber

207:

208:end

Let’s begin looking at this example from back to front. First, a block (lines 187-208) out-side the ImageWindow class definition says that if we are running this application fromthe command line (if $0==__FILE__ .. end), create an instance of ImageWindowinto the local variable window (lines 189-191).We map an interrupt handler to amethod (onCmdQuit) within the instance (line 194). Finally, we give the window its ownthread of execution (line 203) and clean up the Jabber connection as the applicationexits.

187:if $0==__FILE__

188: # Make application

189: application = FXApp.new(“ImageViewer”, “FoxTest”)

190: # Make window

191: window = ImageWindow.new(application)

192:

193: # Handle interrupts to terminate program gracefully

194: application.addSignal(“SIGINT”, window.method(:onCmdQuit))

195:

196: # Create it

197: application.create

198:

199: # Connect to Jabber

200: window.connectToJabber(*ARGV)

201:

202: # Run

203: application.run

204:

Listing 3.1 Continued

05 0672325365 CH03 1/21/05 2:23 PM Page 106

Page 124: Jabber Developer’s Handbook [Sams 2004]

107The <iq> Element

205: # Shutdown Jabber connection

206: window.disconnectFromJabber

207:

208:end

During the stream of execution, we import the jabber4r library and check the num-ber of command-line arguments. Execution then passes to the block above.

1:#!/usr/bin/env ruby

2:

3:

4:

5:require ‘jabber4r/jabber4r’

6:

7:(puts “bitmapper.rb ‘account@host/resource’ ‘password’”) & exit if ARGV.size < 2

8:

9:

All of the code associated with the FXRuby extension is provided by the FOX mod-ule, so we need to start by requiring this feature:

10:require ‘fox’

11:

Because all the FOX Ruby classes are defined under the FOX module, you normallyrefer to them by their “fully qualified” names (that is, names that begin with the Fox::prefix).To avoid extra finger typing, you add an include Fox statement so that all thenames in the FOX module are “included” into the global namespace:

12:include Fox

13:

The ImageWindow class inherits from a FOX MainWindow (line 14); standard practicewith this toolkit is to subclass your own main window from FOX’s MainWindow classand construct its contents in the class’s initialize method (lines 20-63; see http://fxruby.sourceforge.net for toolkit details).

14:class ImageWindow < FXMainWindow

15:

16: attr_reader :jabber_session

17:

18: include Responder

19:

20 .. 63

When we load the image for display, we also invoke sendJabberImage(file):

74: def loadImage(file)

75: file = file.gsub( /\\/ , “/” ) # if you’re on WIN32..

76: sendJabberImage(file)

77: updateImage(file)

78: end

05 0672325365 CH03 1/21/05 2:23 PM Page 107

Page 125: Jabber Developer’s Handbook [Sams 2004]

108 All About Jabber Clients

Let’s look at that in more detail.We get the current session’s roster and iterate overeach roster entry.This is not cached in the jabber_session object, but rather fetchedfrom the server each time to assure currency. (Technically there is some potential from aclient to disconnect during this loop, but in this case, the server will simply drop themessage.) The roster contains a dictionary of roster items, and each iteration yields oneof these, which includes a Jabber ID (Jabber::JID) and a Jabber::Roster::RosterItem::Resource.We check the resources to assure that the client is capable ofreceiving and displaying images (that is, it must have a resource called bitmapper,line 172).

167: def sendJabberImage(imagefile)

169: @jabber_session.roster.each_item do |item|

170: puts item

171: item.each_resource do |resource|

172: if resource.name == “bitmapper”

173: data = nil

174: File.open(imagefile, “rb”) {|file| data = file.read}

175: data = [data].pack(“m”)

176: @jabber_session.new_chat_message(item.jid.to_s+”/bitmapper”)

.set_subject(File.basename(imagefile)).set_body(data).send

177: end

178: end

179: end

180: end

We create a Base64-encoded version of the file (line 175), and send it via the jab-ber_session object.We set the body of the message to the Base64 data (line 176) andsend it.The construction of the message, the setting of a subject, and the message sendare all chained in this terse call.

Note that even though we send a chat-typed message, we set the subject to a file-name. Setting subject in a chat message is unusual, but as we have pointed out previously,it is not forbidden by the protocol.You will see next how this becomes useful at thereceiving instance.

On receipt of the message, an instance fires its connectToJabber method. It pulls asuggested filename from the message subject, stores the decoded Base64 data into the file(line 152), then forces the FOX application to read the file and construct and display theimage (line 155).

147: def connectToJabber(account, account_password)

148: begin

149: @jabber_session = Jabber::Session.bind_digest(“#{account}”,“#{account_password}”)

150: @jabber_session.add_message_listener do |message|

151: File.open(“TMP”+message.subject, “wb”) do |file|

152: file.write message.body.unpack(“m”)[0]

153: file.flush

05 0672325365 CH03 1/21/05 2:23 PM Page 108

Page 126: Jabber Developer’s Handbook [Sams 2004]

109The <iq> Element

154: end

155: updateImage(“TMP”+message.subject)

156: File.delete(“TMP”+message.subject)

157:

158: end

We catch exceptions in lines 159-165:

159: rescue

160: puts “Account does not exist: #{account} - #{account_password}”

161: puts $!

162: puts $!.backtrace

163: exit

164: end

165: end

166:

As a part of application cleanup (on exit), we disconnect the session, if one exists(line 183).

181:

182: def disconnectFromJabber

183: @jabber_session.close if @jabber_session

184: end

185:end

186:

This example uses the very complete jabber4r library, and as you can see, it offers anumber of excellent high-level capabilities, including its own thread handling.

We show this example in Ruby because of the language’s elegance and succinctness,but it serves to instruct for any other language implementation as well.

If you prefer a Python example, here’s the similar application that uses PythonCard, avery nice GUI builder (http://pythoncard.sourceforge.net), one of several GUIs forPython.

It looks slightly different—there are no command-line parameters and there is a sepa-rate login screen.We show all source for the three source files (pictureViewer.py,jabberLogin.py, and jabberHandler.py) composing the application for reference inListings 3.2, 3.3, and 3.4 respectively; however, our comments apply mainly to theJabberHandler.

NoteTo exercise this example, you need to download both the PythonCard application framework (from

http://pythoncard.sourceforge.net) and the wxPython library (from http://www.

wxpython.org). Just use the setup.py with each download to install them in the Python library

folder.

05 0672325365 CH03 1/21/05 2:23 PM Page 109

Page 127: Jabber Developer’s Handbook [Sams 2004]

110 All About Jabber Clients

Figure 3.12 PythonCard ImageViewer implementation.

Listing 3.2 pictureViewer.py

1:import sys, re, os, string

2:from PythonCardPrototype import clipboard, dialog,

➥ graphic, log, model, EXIF

3:from wxPython import wx

4:import os, sys

5:import jabberLogin

6:from jabberHandler import JabberHandler

7:

8:class PictureViewer(model.Background):

9:

10: def on_openBackground(self, event):

11: self.ignoreSizeEvent = 1

12:

13: self.x = 0

14: self.y = 0

15: self.filename = None

16: self.bmp = None

17:

18: bgSize = self.getSize()

19: bufSize = self.GetClientSize()

20: widthDiff = bgSize[0] - bufSize[0]

21: heightDiff = bgSize[1] - bufSize[1]

22: displayRect = wx.wxGetClientDisplayRect()

23: self.maximizePosition = (displayRect[0], displayRect[1])

24: self.maximumSize = (displayRect[2] - widthDiff,

➥ displayRect[3] - heightDiff)

25:

26: if len(sys.argv) > 1:

27: # accept a file argument on the command-line

28: filename = os.path.abspath(sys.argv[1])

29: log.info(‘pictureViewer filename: ‘ + filename)

05 0672325365 CH03 1/21/05 2:23 PM Page 110

Page 128: Jabber Developer’s Handbook [Sams 2004]

111The <iq> Element

30: if not os.path.exists(filename):

31: filename =

os.path.abspath(os.path.join(self.stack.app.

➥ startingDirectory, sys.argv[1]))

32: if os.path.isfile(filename):

33: self.openFile(filename)

34:

35: if self.filename is None:

36: self.fitWindow()

37: self.jabberHandler = None

38:

39: self.Show(1)

40:

41:

42: def on_idle(self, event):

43: self.ignoreSizeEvent = 0

44: if self.jabberHandler is not None

➥ and self.jabberHandler.isConnected():

45: self.jabberHandler.Process()

46: def on_size(self, event):

47: if self.bmp is not None and not self.ignoreSizeEvent:

48: oldSize = self.bmp.getSize()

49: newSize = self.GetClientSize()

50: widthScale = newSize[0] / (0.0 + oldSize[0])

51: heightScale = newSize[1] /(0.0 + oldSize[1])

52: self.displayFileScaled(widthScale, heightScale, 1)

53: def sizeScaled(self, size, widthScale, heightScale):

54: return ((int(size[0] * widthScale), int(size[1] * heightScale)))

55: def displayFileScaled(self, widthScale, heightScale, inUserResize=0):

56: if self.filename is not None:

57: bufOff = self.components.bufOff

58: bufOff.autoRefresh = 0

59: # figure out new size for window

60: size = self.bmp.getSize()

61: newSize = self.sizeScaled(size, widthScale, heightScale)

62: bufOff.size = newSize

63:

64: if inUserResize:

65: self.panel.SetSize(newSize)

66: else:

67: self.fitWindow()

68: bufOff.clear()

69:

70: bufOff.autoRefresh = 1

71: bufOff.drawBitmapScaled(self.bmp, 0, 0, newSize)

Listing 3.2 Continued

05 0672325365 CH03 1/21/05 2:23 PM Page 111

Page 129: Jabber Developer’s Handbook [Sams 2004]

112 All About Jabber Clients

72:

73: # attempt to display the file full size if possible

74: # otherwise the bitmap needs to be scaled

75: def displayFile(self):

76: if self.filename is not None:

77: bufOff = self.components.bufOff

78: bufOff.autoRefresh = 0

79: # figure out new size for window

80: bufOff.size = self.bmp.getSize()

81: self.fitWindow()

82: bufOff.clear()

83:

84: bufOff.autoRefresh = 1

85: bufOff.drawBitmap(self.bmp, 0, 0)

86: def fitToScreen(self):

87: oldSize = self.bmp.getSize()

88: newSize = self.maximumSize

89: widthScale = newSize[0] / (0.0 + oldSize[0])

90: heightScale = newSize[1] /(0.0 + oldSize[1])

91: scale = min(widthScale, heightScale)

92: self.displayFileScaled(scale, scale)

93: self.Center()

94: def fitWindow(self):

95: self.ignoreSizeEvent = 1

96: size = self.components.bufOff.size

97: self.panel.SetSize(size)

98: #if self.ignoreSizeEvent == 1:

99: self.SetClientSize(size)

100: def openFile(self, path):

101: self.filename = path

102: f = open(path, ‘rb’)

103: tags=EXIF.process_file(f)

104: f.close()

105: try:

106: # the repr() is something like

107: # (0x0112) Short=8 @ 54

108: # but the str() is just 1, 8, etc.

109: orientation = int(str(tags[‘Image Orientation’]))

110: except:

111: orientation = 1

112: self.bmp = graphic.Bitmap(self.filename)

113: if orientation == 8:

114: # need to rotate the image

115: # defaults to clockwise, 0 means counter-clockwise

116: #print “rotating”

Listing 3.2 Continued

05 0672325365 CH03 1/21/05 2:23 PM Page 112

Page 130: Jabber Developer’s Handbook [Sams 2004]

113The <iq> Element

117: self.bmp.rotate90(0)

118: elif orientation == 6:

119: self.bmp.rotate90(1)

120: size = self.bmp.getSize()

121: title = os.path.split(self.filename)[-1] + “ %d x %d” % size

122: self.SetTitle(title)

123: # if either dimension of the image is beyond our maximum

124: # then display the image fit to the screen

125: if size[0] > self.maximumSize[0] or size[1] > self.maximumSize[1]:

126: self.fitToScreen()

127: else:

128: self.displayFile()

129: def on_menuFileOpen_select(self, event):

130: result = dialog.openFileDialog()

131: if result[‘accepted’]:

132: self.openFile(result[‘paths’][0])

133: if self.jabberHandler is not None and

➥ self.jabberHandler.isConnected():

134: # distribute the picture to all clients on my roster

135: self.jabberHandler.sendToRoster(result[‘paths’][0])

136: def on_menuFileConnectJabber_select(self, event):

137: result = jabberLogin.jabberLogin(self)

138: self.jabberHandler = JabberHandler(self,

➥ jid=result[‘JabberIdText’], password=result[‘PasswordText’],

➥ server=result[‘JabberServerText’])

139: self.jabberHandler.ConnectToJabber()

140: def on_menuFileSaveAs_select(self, event):

141: if self.filename is None:

142: path = ‘’

143: filename = ‘’

144: else:

145: path, filename = os.path.split(self.filename)

146: wildcard = “All files (*.*)|*.*”

147: result = dialog.saveFileDialog(None, “Save As”,

➥ path, filename, wildcard)

148: if result[‘accepted’]:

149: path = result[‘paths’][0]

150: fileType = graphic.bitmapType(path)

151: try:

152: bmp = self.components.bufOff.getBitmap()

153: bmp.SaveFile(path, fileType)

154: return 1

155: except:

156: return 0

157: else:

Listing 3.2 Continued

05 0672325365 CH03 1/21/05 2:23 PM Page 113

Page 131: Jabber Developer’s Handbook [Sams 2004]

114 All About Jabber Clients

158: return 0

159: def on_menuFileExit_select(self, event):

160: self.Close()

161:

162:if __name__ == ‘__main__’:

163: # require JabberID, Password, Server

164: app = model.PythonCardApp(PictureViewer )

165: app.MainLoop()

The pictureViewer.py file is a simple frame for displaying image files. From the com-mand line, invoke it via > python pictureViewer.py, as shown in Listing 3.3.

Listing 3.3 jabberLogin.py

1:from PythonCardPrototype import model, res

2:import os

3:

4:class JabberLogin(model.CustomDialog):

5: def __init__(self, parent):

6: model.CustomDialog.__init__(self, parent)

7:

8:

9: def jabberLogin(parent):

10: dlg = JabberLogin(parent)

11: dlg.showModal()

12: result = {‘accepted’:dlg.accepted()}

13: result[‘JabberIdText’] = dlg.components.JabberIdText.text

14: result[‘PasswordText’] = dlg.components.PasswordText.text

15: result[‘JabberServerText’] = dlg.components.JabberServerText.text

16:

17: dlg.destroy()

18: return result

JabberLogin.py shown in Listing 3.3 is a simple dialog box for collecting the appropri-ate connection information from the user. It’s shown in Figure 3.12. PictureViewer, theparent frame, calls JabberLogin whenever the user selects Connect to Jabber from theFile menu:

(in pictureViewer.py)

136: def on_menuFileConnectJabber_select(self, event):

137: result = jabberLogin.jabberLogin(self)

138: self.jabberHandler =

➥ JabberHandler(self, jid=result[‘JabberIdText’],

➥ password=result[‘PasswordText’], server=result[‘JabberServerText’])

139: self.jabberHandler.ConnectToJabber()

Listing 3.2 Continued

05 0672325365 CH03 1/21/05 2:23 PM Page 114

Page 132: Jabber Developer’s Handbook [Sams 2004]

115The <iq> Element

When the GUI user logs into a Jabber connection, the JabberHandler is called. It’sthe class that implements and encapsulates all the Jabber details for PictureViewer(pictureViewer.py, lines 138–139, and lines 42–45.) Listing 3.4 shows it in its entiretyand then we deconstruct its significant implementation in detail.

Listing 3.4 jabberHandler.py

1:import jabber

2:import sys

3:import sha

4:import sys, re, os, string, base64

5:

6:class JabberHandler:

7: def __init__(self, parent, jid=None, password=None, server=’localhost’,

resource=”pictureviewer”):

8: self.parent = parent

9: self.jid = jid

10: self.password = password

11: self.server = server

12: self.roster = None

13: self.resource = resource

14: self.ConnectToJabber()

15: self.counter = 0

16:

17:

18: def ConnectToJabber(self):

19: self.con = jabber.Client(host=self.server, debug=0, port=5222,

➥ log=sys.stderr)

20: try:

21: self.con.connect()

22: self.connected = True

23: except IOError, e:

24: print “Couldn’t connect: %s” % e

25: sys.exit(0)

26: else:

27: print “\nConnected\n”

28:

29: self.con.setMessageHandler(self.messageCB)

30: self.con.setPresenceHandler(self.presenceCB)

31: self.con.setIqHandler(self.iqCB)

32:

33: if self.con.auth(self.jid,self.password,self.resource):

34: print “Logged in as %s to server %s” % ( self.jid,self.server)

35: else:

36: print “ERR -> “, self.con.lastErr, self.con.lastErrCode

37: sys.exit(1)

38: self.con.sendInitPresence()

05 0672325365 CH03 1/21/05 2:23 PM Page 115

Page 133: Jabber Developer’s Handbook [Sams 2004]

116 All About Jabber Clients

39: self.roster = self.con.requestRoster()

40: summary = self.roster.getSummary()

41: print “\nRoster:\n”

42: for name in summary.keys():

43: print “\tname—>”, name,

44: print self.roster.getOnline(name)

45: print “\n”

46:

47: jids = self.roster.getJIDs()

48: for jid in jids:

49: print “JID—>”, jid

50: print “\n”

51: print “raw =”, self.roster.getRaw()

52:

53: def isConnected(self):

54: return self.connected

55: def sendToRoster(self, path):

56: print “sending to roster”, path

57: ## get from file and encode...

58: f = open(path, ‘rb’)

59: theData = f.read()

60: base64Data = base64.encodestring(theData)

61: for jid in self.roster.getJIDs():

62: print “send pict —> JID “, jid

63: msg = jabber.Message(jid,base64Data)

64: msg.setSubject(os.path.basename(path))

65: self.con.send(msg)

66:

67:

68:

69: def iqCB(self, iq):

70: “””Called when an iq is recieved, we just let the library handle it at the➥ moment”””

71: print “iqCB”, str(iq)

72:

73:

74: def presenceCB(self, con, prs):

75: “””Called when a presence is received”””

76: print “\npresenceCB\n”, str(prs)

77: who = str(prs.getFrom())

78: type = prs.getType()

79: if type == None: type = ‘available’

80:

81: # subscription request:

82: # - accept their subscription

Listing 3.4 Continued

05 0672325365 CH03 1/21/05 2:23 PM Page 116

Page 134: Jabber Developer’s Handbook [Sams 2004]

117The <iq> Element

83: # - send request for subscription to their presence

84: if type == ‘subscribe’:

85: print “subscribe request from %s” % who

86: con.send(jabber.Presence(to=who, type=’subscribed’))

87: con.send(jabber.Presence(to=who, type=’subscribe’))

88:

89: # unsubscription request:

90: # - accept their unsubscription

91: # - send request for unsubscription to their presence

92: elif type == ‘unsubscribe’:

93: print “unsubscribe request from %s” % who

94: self.con.send(jabber.Presence(to=who, type=’unsubscribed’))

95: self.con.send(jabber.Presence(to=who, type=’unsubscribe’))

96:

97: elif type == ‘subscribed’:

98: print “we are now subscribed to %s” % who

99:

100: elif type == ‘unsubscribed’:

101: print “we are now unsubscribed to %s” % who

102:

103: elif type == ‘available’:

104: print (“%s is available (%s / %s)” % \

105: (who, prs.getShow(), prs.getStatus()))

106: elif type == ‘unavailable’:

107: print (“%s is unavailable (%s / %s)” % \

108: (who, prs.getShow(), prs.getStatus()))

109:

110:

111: def messageCB(self, con, msg):

112: #def messageCB(self, msg):

113: print “\nmessageCB\n”

114: ###

115:

116: theData = base64.decodestring(msg.getBody())

117: suggestedFileName = “TMP”+msg.getSubject()

118: print suggestedFileName

119: f = file(suggestedFileName, “wb”)

120: f.write(theData)

121: f.close()

122: self.parent.openFile(suggestedFileName)

123: ## delete the tmp file??

124:

125: def Process(self):

126: self.con.process(0)

Listing 3.4 Continued

05 0672325365 CH03 1/21/05 2:23 PM Page 117

Page 135: Jabber Developer’s Handbook [Sams 2004]

118 All About Jabber Clients

Here we’ll deconstruct only the JabberHandler class.The strategy the main applicationemploys is that whenever it has idle cycles, it checks to see whether it has a handle tothe JabberHandler and the user has connected to Jabber (if self.jabberHandler is notNone and self.jabberHandler.isConnected()). If so, then it calls theJabberHandler.Process() method:

(in PictureViewr.py)

42: def on_idle(self, event):

43: self.ignoreSizeEvent = 0

44: if self.jabberHandler is not None and self.jabberHandler.isConnected():

45: self.jabberHandler.Process()

This in turn invokes the process() method of the underlying jabber and xmlstreamclasses (in jabber.py).This way the application can check regularly for incoming mes-sages without blocking UI.A much better way to do this is to spawn a separate thread ofexecution for the JabberHandler, and use a Python Message Queue for the halves of theapplication to communicate. For simplicity, we did not do this here, but you should beaware that design strategy for intra-process communication can often have a significantimpact on perceived performance.

NoteThere is a good discussion of this particular topic at

http://pythoncard.sourceforge.net/timers-threads.html.

When the GUI user logs into a Jabber connection, this is the class that implements theJabber handling.

The application imports needed features from the Jabber library and a few others:

1:import jabber

2:import sys

3:import sha

4:import os, base64

5:

The constructor needs the parent application’s handle (the GUI), a user ID, a servername, and a password.These were all garnered from the GUI and passed to the constructor.

6:class JabberHandler:

7: def __init__(self, parent, jid=None, password=None, server=’localhost’,

resource=”pictureviewer”):

8: self.parent = parent

9: self.jid = jid

10: self.password = password

11: self.server = server

12: self.roster = None

13: self.resource = resource

14: self.ConnectToJabber()

15:

05 0672325365 CH03 1/21/05 2:23 PM Page 118

Page 136: Jabber Developer’s Handbook [Sams 2004]

119The <iq> Element

The attempt to connect with the server is implemented here:

16: def ConnectToJabber(self):

17: self.con = jabber.Client(host=self.server, debug=False,➥ port=5222,log=sys.stderr)

18: try:

19: self.con.connect()

20: self.connected = True

21: except IOError, e:

22: print “Couldn’t connect: %s” % e

23: sys.exit(0)

24:

We set handlers for the various callbacks, and use our own overrides for the defaultmethods:

25: self.con.setMessageHandler(self.messageCB)

26: self.con.setPresenceHandler(self.presenceCB)

27: self.con.setIqHandler(self.iqCB)

28:

If we fail, the application exists.A more mature application might do something suchas raise an error dialog and give the user another chance to log in.

29: if not self.con.auth(self.jid,self.password,self.resource):

30: print “ERR -> “, self.con.lastErr, self.con.lastErrCode

31: sys.exit(1)

32: self.con.sendInitPresence()

33: self.roster = self.con.requestRoster()

34: summary = self.roster.getSummary()

These lines show off the Jabber library capabilities and are not needed to run theapplication.When lines 36–41 are executed:

35:### didactic code, not needed for application

36: print “\nRoster:\n”

37: for name in summary.keys():

38: print “\tname—>”, name,

39: print self.roster.getOnline(name)

40: print “\n”

41:

the returned result looks like:

Roster:

name—> Pix2@localhost offline

name—> Pix3@localhost offline

05 0672325365 CH03 1/21/05 2:23 PM Page 119

Page 137: Jabber Developer’s Handbook [Sams 2004]

120 All About Jabber Clients

And this:

42: jids = self.roster.getJIDs()

43: for jid in jids:

44: print “JID—>”, jid

45: print “\n”

46: print “raw =”, self.roster.getRaw()

produces:

JID—> Pix2@localhost

JID—> Pix3@localhost

as well as the raw roster, which is a Python dictionary of dictionaries. Each JabberId(Pix2@localhost, Pix3@localhost) keys a dictionary of the relevant roster items (status,availability, and so on).

raw = {

u’Pix2@localhost’:

{‘status’: None,

‘name’: u’Pix2’,

‘groups’: [],

‘online’: ‘offline’,

‘ask’: None,

‘show’: None,

‘sub’: u’both’

},

u’Pix3@localhost’:

{‘status’: None,

‘name’: u’Pix3’,

‘groups’: [u’Unfiled’],

‘online’: ‘offline’,

‘ask’: None,

‘show’: None,

‘sub’: u’both’

}

}

The sendToRoster method gets a cached roster and then sends the image to rostermembers:

50: def sendToRoster(self, path):

51: print “sending to roster”, path

52: ## get from file and encode...

53: f = open(path, ‘rb’)

54: theData = f.read()

55: base64Data = base64.encodestring(theData)

56: for jid in self.roster.getJIDs():

57:

05 0672325365 CH03 1/21/05 2:23 PM Page 120

Page 138: Jabber Developer’s Handbook [Sams 2004]

121The <iq> Element

58: msg = jabber.Message(jid,base64Data)

59: msg.setSubject(os.path.basename(path))

60: self.con.send(msg)

Why did we encode the binary bits (line 55 and also on line 175 of Listing 3.1 theearlier Ruby example), and then pass that to the message constructor?

Well, remember that the data in the body of a message must conform to the rule forordinary PCDATA.The only reasonable choice is to turn any alien format (alien fromthe standpoint of XML) into Base64 ASCII data. On receipt, the destination client willsimply decode the Base64 data and use it to create a binary object.

We implement a minimal presence handling here, just to keep the roster peers insync.

69: def presenceCB(self, con, prs):

70: “””Called when a presence is recieved”””

71: print “\npresenceCB\n”, str(prs)

72: who = str(prs.getFrom())

73: type = prs.getType()

74: if type == None: type = ‘available’

75:

76: # subscription request:

77: # - accept their subscription

78: # - send request for subscription to their presence

79: if type == ‘subscribe’:

80: con.send(jabber.Presence(to=who, type=’subscribed’))

81: con.send(jabber.Presence(to=who, type=’subscribe’))

82:

83: # unsubscription request:

84: # - accept their unsubscription

85: # - send request for unsubscription to their presence

86: elif type == ‘unsubscribe’:

87: self.con.send(jabber.Presence(to=who, type=’unsubscribed’))

88: self.con.send(jabber.Presence(to=who, type=’unsubscribe’))

89:

We implement a message callback (messageCB) to decode the message body contents(line 92), grab the message subject, use it for a temporary filename (line 93), write thedecoded contents of the message (lines 94–96), and notify a method in the parent thatthere is a new image for display.The parent has a method (self.parent.openFile) thatdoes just this.

90: def messageCB(self, con, msg):

91:

92: theData = base64.decodestring(msg.getBody())

93: suggestedFileName = “TMP”+msg.getSubject()

94: f = file(suggestedFileName, “wb”)

95: f.write(theData)

96: f.close()

97: self.parent.openFile(suggestedFileName)

05 0672325365 CH03 1/21/05 2:23 PM Page 121

Page 139: Jabber Developer’s Handbook [Sams 2004]

122 All About Jabber Clients

Finally, whenever the parent process invokes its JabberHandler’s Process method(pictureViewer.py, lines 42–45), the underlying Jabber process() method fires.

98:

99: def Process(self):

100: self.con.process(0)

You should use the Pythocard resource editor to produce pictureViewer.rsrc.pyand jabberLogin.rsrc.py files corresponding to both the pictureViewer.py andjabberLogin.py files. It would stray too far off topic to discuss this further. If you needadditional help in this step (somewhat unlikely,) please email the authors.

Remember before running either the Ruby or Python example to modify your jab-ber.xml configuration file to allow larger file transfers (see Chapter 2).A “real” applica-tion such as this should have to break large binary data into many small messages to bet-ter conform to the Jabber model. Sending too large a single message would both overtaxthe server and possibly even break a client. Remember that the Jabber message modelprefers short “IM-style” messages.Additionally, the Jabber server prefers many clientsexchanging short messages as well.

NoteAlso note that the server doesn’t respond well to message flooding. Thus, even if you broke the message up

into several pieces and tried to send them simultaneously, the server still might complain to your application

for violating the karma settings.

SummaryThis chapter looked at a client-side view of the Jabber world. By now, if you’ve absorbedsome of what we’ve talked about, you must be thinking that the Jabber server is rather acomplex and capable structure.We’d say that you’re exactly right! In the next chapter wemove to deconstructing the server from an architectural perspective before we go on inChapter 5 to look at a few of the things you can build using the client, server, and mes-sage concepts embodied in Jabber.

05 0672325365 CH03 1/21/05 2:23 PM Page 122

Page 140: Jabber Developer’s Handbook [Sams 2004]

4Jabber Server Architecture

Architecture is the learned game, correct and magnificent, of forms assembled in the light.

Le Corbusier

NOW THAT YOU’VE SEEN THE PROTOCOLS involved in Jabber messaging, in this chapterwe look at how the Jabber reference server implementation is put together and how themessages flow within and between servers.

NOTEThe server that we will discuss and use for our examples is the jabberd server implemented in C. It is the

reference implementation of a Jabber server, but it’s not the only one available. There is a commercial server

available from Jabber, Inc., and you can find more open-source servers on the JabberStudio Web site at

http://www.jabberstudio.org.

High-Level ArchitectureThe Jabber server (jabberd) is made up of several components that interact throughexchanging messages on an internal message bus.The downloaded default configurationof jabberd includes the components that are needed to get an instant messaging serverup and running:

n Jabber Session Manager (JSM).The JSM manages the registration of new useraccounts, authenticates users, and manages presence information. It is by far thelargest, most complicated component shipped with jabberd.

n c2s (Client-to-Server). c2s handles connections between the server and itsclients. Its main job is formatting and routing messages between clients and othercomponents (almost always JSM in the case of simple instant messaging).

06 0672325365 CH04 1/21/05 2:23 PM Page 123

Page 141: Jabber Developer’s Handbook [Sams 2004]

124 Chapter 4 Jabber Server Architecture

n s2s (Server-to-Server). s2s handles connections between the server and otherservers.The protocol for s2s connections is slightly different than c2s connectionsand this component speaks that protocol.

n xdb (XML Database). xdb responds to messages to store and retrieve data. It’s theshared persistent storage mechanism for the Jabber server.

n Logger. Logger services receive messages from other components intended totrack the server’s actions. Logged messages include things such as user logins andlogouts, errors, and so on.The standard jabberd configuration includes two loggers:elogger for errors and rlogger for all other system events.

n dnsrv (DNS Service). dnsrv resolves server names to IP addresses. Server namesare almost always DNS host names, so this is a straightforward function.

A couple common additional components aren Jabber User Directory (JUD). The JUD component provides services that enable

clients to publish their contact information and to query for other clients’ infor-mation.

n Conferencing (conf). The conferencing component allows clients to join groupsin which messages sent to the group are delivered to all other clients that aremembers of that group.This is commonly called the “chat room” function, but ithas uses outside instant messaging.

Each component has a Jabber ID (JID) that distinguishes it from other components.These components exchange messages among themselves over a data bus that routesmessages to the appropriate component based on the type of message and the destina-tion JID.

In addition to the <message>, <presence>, and <iq> messages that are exchangedbetween clients and servers, server components also exchange three other types of mes-sages:

n <log>—These messages contain server status information and are delivered to thelog component.

n <xdb>—These messages contain either queries or persistent data that is beingstored to or retrieved from the persistent store.The type attribute is get forqueries, set for stores, and result for query responses.

n <route>—Route messages wrap other messages for delivery to other components.They are like envelopes in that they can be used to enclose a message for deliveryto a different address.

Figure 4.1 shows the components of a typical Jabber server. Notice how the connectionsbetween the components are all mediated by the component bus.This design is key tothe Jabber server’s flexibility and scalability. It allows components to be configured andreplaced independently and even allows them to be distributed to multiple computers ifnecessary.

06 0672325365 CH04 1/21/05 2:23 PM Page 124

Page 142: Jabber Developer’s Handbook [Sams 2004]

125Messages and Sessions

Figure 4.1 The jabberd component bus.

The following sections examine in detail examples of how messages flow among com-ponents.

Messages and SessionsFirst, take the simple case of a client sending a message to another client on the sameserver while both clients are online.A client’s connection to the server is maintained bythe c2s component, so this interaction starts with the client initiating the message bysending its message packet to the c2s component:

1) user1 → c2s

<message id=’JCOM_13’ to=’user2@my-jabber’>

<thread>ED52C9B4928820B2ABC834876ADF084D</thread>

<subject>Greetings!</subject>

<body>Hello, user2.</body>

</message>

This message contains just the addressing information as an attribute to the <message>tag, and the message payload in the <thread>, <subject>, and <body> elements.

The c2s component could just send this message to user2, but it needs to make surethat user2 doesn’t filter messages from user1. Message filter information is managed bythe JSM (known internally to the server by the service name sessions), so c2s wraps

���

������ ���� ������

��� ���� ��� ����

��������� ��

��� �� � ���

���

� � � �

06 0672325365 CH04 1/21/05 2:23 PM Page 125

Page 143: Jabber Developer’s Handbook [Sams 2004]

126 Chapter 4 Jabber Server Architecture

the <message> packet in a <route> packet for internal delivery between components.The resulting packet looks like this:

2) c2s → sessions

<route to=’user1@my-jabber/81F9BB0’ from=’16@c2s/813F5A0’>

<message id=’JCOM_13’ to=’user2@my-jabber’>

<thread>ED52C9B4928820B2ABC834876ADF084D</thread>

<subject>Greetings!</subject>

<body>Hello, user2.</body>

</message>

</route>

When the JSM receives this packet, it needs to fetch user2’s filter information. Like allpersistent data, filters are stored by the xdb component.The JSM forms and sends an<xdb> packet like this:

3) sessions → xdb

<xdb type=’get’ to=’user2@my-jabber’ from=’sessions’

ns=’jabber:iq:filter’ id=’48’/>

In this packet, the value of the type attribute is get, which identifies this as a query;the value of the to attribute is user2@my-jabber, which identifies the user whose infor-mation is being queried; and the value of the ns (name space) attribute is jabber:iq:filter, which identifies what information about that user is being requested.The xdbcomponent looks up the filter settings for user2 and replies with a packet like this:

4) xdb → sessions

<xdb type=’result’ to=’sessions’ from=’user2@my-jabber’

ns=’jabber:iq:filter’ id=’48’>

<query xmlns=’jabber:iq:filter’>

<rule name=’Auto Greeting’>

<subject>How are you</subject>

<reply>Fine, thanks.</reply>

</rule>

</query>

</xdb>

From this packet, we can see that user2 has set up one filter to automatically reply tomessages that have a subject that includes the phrase How are you.The JSM checks thecurrent message against the filter and decides that it’s not applicable, so the message is tobe delivered normally to user2.The JSM wraps the message back into a route packet fordelivery to c2s like this:

5) sessions → c2s

<route from=’user2@my-jabber/8141D20’ to=’17@c2s/81FB5C8’>

<message id=’JCOM_13’ to=’user2@my-jabber’ from=’user1@my-jabber/Work’>

<thread>ED52C9B4928820B2ABC834876ADF084D</thread>

06 0672325365 CH04 1/21/05 2:23 PM Page 126

Page 144: Jabber Developer’s Handbook [Sams 2004]

127Messages and Sessions

<subject>Greetings!</subject>

<body>Hello, user2.</body>

</message>

</route>

The c2s component unwraps the message and delivers it to user2 in a packet like this:

6) c2s → user2

<message id=’JCOM_13’ to=’user2@my-jabber’ from=’user1@my-jabber/Work’>

<thread>ED52C9B4928820B2ABC834876ADF084D</thread>

<subject>Greetings!</subject>

<body>Hello, user2.</body>

</message>

So six messages are required to send a message from user1 to user2.The exchangeinvolves the c2s, sessions, and xdb components.To reiterate, this interaction is shownin Figure 4.2.

user2xdbsessionsuser1 c2s

1 2

4

6

5

3 lookupfilters

Figure 4.2 Simple (one-server) messaging.

Remote MessagingNow it’s time to examine a slightly more complicated example. In this example, user1again sends a message to user2, but this time user2 is connected to a different server.

User1 creates this message and sends it to the c2s component of its local server:

1) user1 → c2s (my-jabber)

<message id=’JCOM_4’ to=’user2@other-server’>

<thread>05C2A7562274838DBA443AD698271C82</thread>

<subject>Greetings!</subject>

06 0672325365 CH04 1/21/05 2:23 PM Page 127

Page 145: Jabber Developer’s Handbook [Sams 2004]

128 Chapter 4 Jabber Server Architecture

<body>Hello, user2</body>

</message>

As before, the c2s component wraps this message in a <route> packet and sends it tobe delivered internally to the JSM:

2) c2s (my-jabber) → sessions (my-jabber)

<route to=’user1@my-jabber/81F9A50’ from=’16@c2s/813F5A0’>

<message id=’JCOM_4’ to=’user2@other-server’>

<thread>05C2A7562274838DBA443AD698271C82</thread>

<subject>Greetings!</subject>

<body>Hello, user2</body>

</message>

</route>

The JSM sees that this is a message for another server, so it unwraps it from the<route> packet and sends this packet to be delivered internally to the s2s (server-to-server) component:

3) sessions (my-jabber) → s2s (my-jabber)

<message id=’JCOM_4’ to=’user2@other-server’ from=’user1@my-jabber/Work’>

<thread>05C2A7562274838DBA443AD698271C82</thread>

<subject>Greetings!</subject>

<body>Hello, user2</body>

</message>

We’ll assume that the two servers have already established a connection and authenti-cated each other with the dialback protocol (see Chapter 6 for details on dialback), solocal s2s can just use that connection to send the message to the other s2s:

4) s2s (my-jabber) → s2s (other-server)

<message id=’JCOM_4’ to=’user2@other-server’ from=’user1@my-jabber/Work’>

<thread>05C2A7562274838DBA443AD698271C82</thread>

<subject>Greetings!</subject>

<body>Hello, user2</body>

</message>

This packet is received by the remote s2s and sent for routing to the remote JSM:

5) s2s (other-server) → sessions (other-server)

<message id=’JCOM_4’ to=’user2@other-server’ from=’user1@my-jabber/Work’>

<thread>05C2A7562274838DBA443AD698271C82</thread>

<subject>Greetings!</subject>

<body>Hello, user2</body>

</message>

All that remains at this point is to deliver the message locally, so it’s much like the firstexample.The JSM needs to run the message against user2’s filters, so it queries xdb:

06 0672325365 CH04 1/21/05 2:23 PM Page 128

Page 146: Jabber Developer’s Handbook [Sams 2004]

129Messages and Sessions

6) sessions (other-server) → xdb (other-server)

<xdb type=’get’ to=’user2@other-server’ from=’sessions’

ns=’jabber:iq:filter’ id=’41’/>

The xdb component looks up the user’s filters and responds with a packet like this:

7) xdb (other-server) → sessions (other-server)

<xdb type=’result’ to=’sessions’ from=’user2@other-server’

ns=’jabber:iq:filter’ id=’41’/>

In this case, user2 has no filters, so the JSM can just wrap the message in a <route>packet and send it for delivery to c2s:

8) sessions (other-server) → c2s (other-server)

<route from=’user2@other-server/A191D68’ to=’15@c2s/A0DEAD8’>

<message id=’JCOM_4’ to=’user2@other-server’ from=’user1@my-jabber/Work’>

<thread>05C2A7562274838DBA443AD698271C82</thread>

<subject>Greetings!</subject>

<body>Hello, user2</body>

</message>

</route>

The c2s component receives this route packet, unwraps the enclosed message, andsends it over the connection it maintains with user2:

9) c2s (other-server) → user2

<message id=’JCOM_4’ to=’user2@other-server’ from=’user1@my-jabber/Work’>

<thread>05C2A7562274838DBA443AD698271C82</thread>

<subject>Greetings!</subject>

<body>Hello, user2</body>

</message>

So it takes a few more messages, but remote delivery of a message is not too much morecomplicated than local delivery.The sequence diagram in Figure 4.3 outlines these steps.

sessions(my-jabber)

user1s2s

(my-jabber)s2s

(my-jabber)s2s

(other-server) user2

12

34

5

7

8

6

9

lookupfilters

sessions(other-server)

xdb(other-server)

c2s(other-server)

Figure 4.3 Remote (server-to-server) messaging.

06 0672325365 CH04 1/21/05 2:23 PM Page 129

Page 147: Jabber Developer’s Handbook [Sams 2004]

130 Chapter 4 Jabber Server Architecture

Client InitializationWhen a typical instant messaging client logs in to a server, it does several things to getitself initialized:

1. It establishes a connection to the server and identifies itself as a Jabber client.

2. It authenticates to the server (that is, it somehow proves to the server who it is).

3. It asks for the list of services (agents) available on that server.

4. It asks for its roster (which is stored on the server).

5. It sets its presence display for other clients to see.

This section traces in detail the messages that are sent to make these things happen.

Step 1: Connecting

First, of course, the client connects to the c2s component.They exchange stream head-ers over that connection so the client can verify that it’s talking to a Jabber server andthe c2s component can verify that it’s talking to a Jabber client.The exchanged messageslook like this:

1) Client → c2s

<?xml version=’1.0’?>

<stream:stream to=”my-jabber” xmlns=”jabber:client”

xmlns:stream=”http://etherx.jabber.org/streams”>

The details aren’t too important, but the to attribute tells the server the name of theserver to which the client thinks it’s talking (“my-jabber” in this case), and the xmlns

attribute tells the server that this is a Jabber client:

2) c2s → Client

<?xml version=’1.0’?>

<stream:stream xmlns:stream=’http://etherx.jabber.org/streams’

id=’3DF21965’ xmlns=’jabber:client’ from=’my-jabber’>

This stream header, sent from the c2s component to the client, acknowledges that theclient is talking to a Jabber server, and that the server’s name is “my-jabber.”The id attrib-ute provides an identifier for this connection that the client will need to use when itauthenticates. Figure 4.4 shows the interactions between the client and c2s for this action.

Step 2: Authenticating

So far, all the messages have been between the c2s component and the client, but actual-ly starting a session for the client involves several other components. It begins when theclient requests the instructions for logging in (authenticating) with this IQ packet:

1) Client → c2s

<iq type=”get” id=”JCOM_2”>

<query xmlns=”jabber:iq:auth”>

06 0672325365 CH04 1/21/05 2:23 PM Page 130

Page 148: Jabber Developer’s Handbook [Sams 2004]

131Messages and Sessions

Figure 4.4 Connection interactions.

The c2s component doesn’t handle authentication, so it wraps this IQ message in a<route> packet, sets its type to auth (for “authentication”) and submits it to delivery toany component that handles auth messages.This message gets delivered to the JSMcomponent (known internally by the service name sessions):

2) c2s → sessions

<route type=’auth’ to=’user1@my-jabber’ from=’16@c2s/813FAF0’>

<iq type=’get’ id=’JCOM_2’>

<query xmlns=’jabber:iq:auth’>

<username>user1</username>

</query>

</iq>

</route>

Notice how c2s uses the from attribute JID in the <route> packet to hold informa-tion about the connection.This information will come back in the to attribute of thereply, enabling c2s to associate the reply with the original request.This is a commontrick you’ll see used many places.

When JSM (“sessions”) receives this message, it needs to get some information aboutuser1@my-jabber to be able to reply to this message. It needs to query the XML data-base to get the login information required for this user, so JSM generates an <xdb> mes-sage like this:

3) sessions → xdb

<xdb type=’get’ to=’user1@my-jabber’ from=’sessions’

ns=’jabber:iq:auth’ id=’1’/>

������ ���

<username>user1</username>

</query>

</iq>

06 0672325365 CH04 1/21/05 2:23 PM Page 131

Page 149: Jabber Developer’s Handbook [Sams 2004]

132 Chapter 4 Jabber Server Architecture

The type (get) identifies this as a query.The to and ns attributes are the parametersto the query and are used to route the request to the correct xdb instance, if there ismore than one, and to identify what data will be retrieved.The JSM uses the id attributeto match the response that will follow with this query.

The xdb component receives this request, looks in its database, and if all goes well,responds with a message like this:

4) xdb → sessions

<xdb type=’result’ to=’sessions’

from=’user1@my-jabber’ ns=’jabber:iq:auth’ id=’1’>

<password xmlns=’jabber:iq:auth’>user1</password>

</xdb>

This tells the JSM that user1’s password is user1 (not a very good idea, by the way).JSM also supports a form of authentication called zero-knowledge (see Chapter 6 fordetails), so it requests the zero-knowledge state information also:

5) sessions → xdb

<xdb type=’get’ to=’user1@my-jabber’ from=’sessions’

ns=’jabber:iq:auth:0k’ id=’2’/>

Here the ns=’jabber:iq:auth:0k’ tells xdb the record that JSM needs it to fetch.xdb fetches it and replies with this message:

6) xdb → sessions

<xdb type=’result’ to=’sessions’ from=’user1@my-jabber’

ns=’jabber:iq:auth:0k’ id=’2’>

<zerok xmlns=’jabber:iq:auth:0k’>

<token>3DE4F2A3</token>

<sequence>453</sequence>

<hash>d1d7b5a13677af3f6669538bd9c89a8eee6de5d9</hash>

</zerok>

</xdb>

The <zerok> record element contains three values: token, sequence, and hash, whichthe JSM uses to authenticate the client with zero-knowledge authentication. Now that ithas everything it needs, JSM can respond to the message from c2s:

7) sessions → c2s

<route from=’user1@my-jabber’ to=’16@c2s/813FAF0’ type=’auth’>

<iq type=’result’ id=’JCOM_2’>

<query xmlns=’jabber:iq:auth’>

<username>user1</username>

<digest/>

<password/>

<sequence>452</sequence>

<token>3DE4F2A3</token>

<resource/>

06 0672325365 CH04 1/21/05 2:23 PM Page 132

Page 150: Jabber Developer’s Handbook [Sams 2004]

133Messages and Sessions

</query>

</iq>

</route>

This <route> message wraps an <iq> message that the c2s should send to the client sothe client knows how to authenticate.The packet that c2s sends to the client looks like this:

8) c2s → Client

<iq type=’result’ id=’JCOM_2’>

<query xmlns=’jabber:iq:auth’>

<username>user1</username>

<digest/>

<password/>

<sequence>452</sequence>

<token>3DE4F2A3</token>

<resource/>

</query>

</iq>

Now the client knows what it needs to provide to log in to the server, so it generatesa login packet like this and sends it to c2s:

9) Client → c2s

<iq type=’set’ id=’JCOM_3’>

<query xmlns=’jabber:iq:auth’>

<username>user1</username>

<password>user1</password>

<resource>Work</resource>

</query>

</iq>

As before, c2s wraps this packet in a <route> packet for delivery to the authentica-tion component (that is, the JSM):

10) c2s → sessions

<route type=’auth’ to=’user1@my-jabber/Work’ from=’16@c2s/8143760’>

<iq type=’set’ id=’JCOM_3’>

<query xmlns=’jabber:iq:auth’>

<username>user1</username>

<password>user1</password>

<resource>Work</resource>

</query>

</iq>

</route>

Again this auth message is routed to the JSM component.The JSM already has thepassword from before and can compare it with the one in this packet.They match, so itresponds with a wrapped IQ success packet for the client—by way of c2s:

06 0672325365 CH04 1/21/05 2:23 PM Page 133

Page 151: Jabber Developer’s Handbook [Sams 2004]

134 Chapter 4 Jabber Server Architecture

11) sessions → c2s

<route from=’user1@my-jabber/Work’ to=’16@c2s/8143760’ type=’auth’>

<iq type=’result’ id=’JCOM_5’/>

</route>

c2s unwraps the <route> packet and sends the success IQ packet to the client:

12) c2s → Client

<iq type=’result’ id=’JCOM_5’/>

At this point, the client is authenticated, but there is still some internal housekeepingto do.The c2s notifies the session manager that it should create a session with a packetlike this:

13) c2s → sessions

<route type=’session’ to=’user1@my-jabber/Work’ from=’16@c2s/813F5A0’/>

If it successfully creates the session, the JSM replies:

14) sessions → c2s

<route type=’session’ to=’16@c2s/813F5A0’ from=’user1@my-jabber/81F9BB0’/>

Finally, c2s creates a log entry to make a record of the successful login.

15) c2s → log

<log type=’record’ from=’user1@my-jabber’>login ok 192.168.1.103 Work</log>

Figure 4.5 shows these interactions between the client and server components whenauthenticating.

Step 3: Querying for Agents

At this point, the client is successfully logged in and can send messages or use any otherfunctions of the server.The next thing an IM client may ask for is a list of agents so itcan tell the user what services are available.The client starts the process by sending an IQpacket to c2s:

1) Client → c2s

<iq id=’wjAgents’ to=’my-jabber’ type=’get’>

<query xmlns=’jabber:iq:agents’/>

</iq>

The c2s component wraps that packet in a <route> packet for delivery to the JSM,which maintains the list of browsable agents:

2) c2s → sessions

<route to=’user1@my-jabber/81F9BB0’ from=’16@c2s/813F5A0’>

<iq id=’wjAgents’ to=’my-jabber’ type=’get’>

<query xmlns=’jabber:iq:agents’/>

</iq>

</route>

06 0672325365 CH04 1/21/05 2:23 PM Page 134

Page 152: Jabber Developer’s Handbook [Sams 2004]

135Messages and Sessions

Figure 4.5 Authentication interactions.

Notice again the contents of the from attribute in the <route> element.The host field(c2s) ensures that a reply will be delivered to c2s; all the rest (the username andresource) is used by c2s to associate requests with replies.

The JSM responds with a list of the agents available on the server in an IQ resultpacket wrapped in a <route> packet for c2s:

3) sessions → c2s

<route from=’user1@my-jabber/81F9BB0’ to=’16@c2s/813F5A0’>

<iq id=’wjAgents’ to=’user1@my-jabber/Work’ type=’result’ from=’my-jabber’>

<query xmlns=’jabber:iq:agents’>

<agent jid=’jud.my-jabber’>

<name>Global Mega Corp Directory</name>

<service>jud</service>

<search/>

<register/>

</agent>

<agent jid=’conference.my-jabber’>

<name>Public Chatrooms</name>

<service>public</service>

<groupchat/>

</agent>

</query>

sessionsClient c2s xdb log

12

3

4

5

7

8

6

9

lookup

lookup

15

12

10

13

11

14

06 0672325365 CH04 1/21/05 2:23 PM Page 135

Page 153: Jabber Developer’s Handbook [Sams 2004]

136 Chapter 4 Jabber Server Architecture

</iq>

</route>

c2s unwraps the <route> packet and sends the IQ packet back to the client so itknows about the local JUD directory service and the Public Chatrooms conferencingservice:

4) c2s → Client

<iq id=’wjAgents’ to=’user1@my-jabber/Work’ type=’result’ from=’my-jabber’>

<query xmlns=’jabber:iq:agents’>

<agent jid=’jud.my-jabber’>

<name>Global Mega Corp Directory</name>

<service>jud</service>

<search/>

<register/>

</agent>

<agent jid=’conference.my-jabber’>

<name>Public Chatrooms</name>

<service>public</service>

<groupchat/>

</agent>

</query>

</iq>

Now the client could contact either of those services directly by using the JID pro-vided in this reply. Figure 4.6 shows these interactions as a sequence diagram.

�������������� ��

Figure 4.6 Querying for agents.

Step 4: Fetching the Roster

A user’s roster is stored on the server, so when an IM client connects, one of the firstthings it does is fetch the roster.The IM client displays the roster for the user and uses itto organize presence information from the roster members.As you probably guessed, itstarts with the client sending a packet to c2s:

06 0672325365 CH04 1/21/05 2:23 PM Page 136

Page 154: Jabber Developer’s Handbook [Sams 2004]

137Messages and Sessions

1) Client → c2s

<iq id=’fullroster_6’ type=’get’>

<query xmlns=’jabber:iq:roster’/>

</iq>

The c2s component wraps this in a <route> packet and sends it.The JSM handlesthese packets, so it gets delivered there:

2) c2s → sessions

<route to=’user1@my-jabber/81F9BB0’ from=’16@c2s/813F5A0’>

<iq id=’fullroster_6’ type=’get’>

<query xmlns=’jabber:iq:roster’/>

</iq>

</route>

The JSM stores users’ roster information in xdb, so it generates an <xdb> message tofetch it:

3) sessions → xdb

<xdb type=’get’ to=’user1@my-jabber’ from=’sessions’

ns=’jabber:iq:roster’ id=’32’/>

xdb looks up the requested data (jabber:iq:roster) for the requested user (user1@my-jabber), and replies with a packet like this:

4) xdb → sessions

<xdb type=’result’ to=’sessions’ from=’user1@my-jabber’

ns=’jabber:iq:roster’ id=’32’>

<query xmlns=’jabber:iq:roster’>

<item jid=’user2@other-server’ name=’user2’ subscription=’both’>

<group>My Resources</group>

</item>

</query>

</xdb>

This roster contains one contact (user2@other-server) in the contact group MyResources.The JSM puts this data in the appropriate IQ packet and wraps the IQ in a<route> packet for delivery to c2s:

5) sessions → c2s

<route from=’user1@my-jabber/81F9BB0’ to=’16@c2s/813F5A0’>

<iq id=’fullroster_6’ type=’result’ from=’user1@my-jabber/Work’>

<query xmlns=’jabber:iq:roster’>

<item jid=’user2@other-server’ name=’user2’ subscription=’both’>

<group>My Resources</group>

</item>

</query>

</iq>

</route>

06 0672325365 CH04 1/21/05 2:23 PM Page 137

Page 155: Jabber Developer’s Handbook [Sams 2004]

138 Chapter 4 Jabber Server Architecture

c2s removes the IQ packet from the <route> packet and sends the completed rosterdata to the client:

6) c2s → Client

<iq id=’fullroster_6’ type=’result’ from=’user1@my-jabber/Work’>

<query xmlns=’jabber:iq:roster’>

<item jid=’user2@other-server’ name=’user2’ subscription=’both’>

<group>My Resources</group>

</item>

</query>

</iq>

The sequence diagram for this interaction is shown in Figure 4.7.

�������������� �� ��

��

������

Figure 4.7 Fetching the roster.

Step 5: Setting the Presence

The last step in getting the IM client initialized is setting the user’s presence informa-tion.This tells other clients that the user is online and ready.Again the first message isfrom the client to c2s:

1) Client → c2s

<presence>

<status>Online</status>

</presence>

This little packet gets wrapped in a <route> packet and delivered to the JSM:

2) c2s → sessions

<route to=’user1@my-jabber/81F9BB0’ from=’16@c2s/813F5A0’>

<presence>

<status>Online</status>

</presence>

</route>

06 0672325365 CH04 1/21/05 2:23 PM Page 138

Page 156: Jabber Developer’s Handbook [Sams 2004]

139Messages and Sessions

Now that the client is online, the JSM can send it any messages that have been storedfor it while it was offline. Like all persistent data, these messages are stored in xdb, so theJSM sends an <xdb> message like this:

3) sessions → xdb

<xdb type=’get’ to=’user1@my-jabber’ from=’sessions’

ns=’jabber:x:offline’ id=’33’/>

This client has no offline messages, so xdb responds with a packet with an empty<foo> element like this:

4) xdb → sessions

<xdb type=’result’ to=’sessions’ from=’user1@my-jabber’

ns=’jabber:x:offline’ id=’33’>

<foo xmlns=’jabber:x:offline’/>

</xdb>

At this point, the JSM tries to send any queued offline messages, if any exist.Afterthey are sent, the xdb is updated to clear the old messages with a packet like this:

5) sessions → xdb

<xdb type=’set’ to=’user1@my-jabber’ from=’sessions’

ns=’jabber:x:offline’ id=’34’/>

Notice that the type is set rather than get, to identify this as a database write ratherthan a read. If this is successful, xdb responds with a success result like this:

6) xdb → sessions

<xdb type=’result’ to=’sessions’ from=’user1@my-jabber’

ns=’jabber:x:offline’ id=’34’>

<foo xmlns=’jabber:x:offline’/>

</xdb>

The change in the client’s presence status also means that the clients on its roster needto be notified.The JSM is responsible for this also, so it fetches the client’s roster from xdb:

7) sessions → xdb

<xdb type=’get’ to=’user1@my-jabber’ from=’sessions’

ns=’jabber:iq:roster’ id=’35’/>

xdb responds with the user’s roster as before:

8) xdb → sessions

<xdb type=’result’ to=’sessions’ from=’user1@my-jabber’

ns=’jabber:iq:roster’ id=’35’>

<query xmlns=’jabber:iq:roster’>

<item jid=’user2@other-server’ name=’user2’ subscription=’both’>

<group>My Resources</group>

06 0672325365 CH04 1/21/05 2:23 PM Page 139

Page 157: Jabber Developer’s Handbook [Sams 2004]

140 Chapter 4 Jabber Server Architecture

</item>

</query>

</xdb>

For each subscriber to this client’s presence information, the JSM generates a presencepacket.This client has only one presence subscriber, but it is on a different server(user2@other-server), so the packet generated is delivered to the s2s component forremote delivery:

9) sessions → s2s

<presence from=’user1@my-jabber/Work’ to=’user2@other-server’>

<status>Online</status>

<x xmlns=’jabber:x:delay’ from=’user1@my-jabber/Work’

stamp=’20021207T18:55:38’/>

</presence>

The stamp attribute of the <x> element is a timestamp to record the time that user1’spresence status changed.The local s2s component delivers this packet to the remote s2scomponent:

10) Local s2s → Remote s2s

<presence from=’user1@my-jabber/Work’ to=’user2@other-server’>

<status>Online</status>

<x xmlns=’jabber:x:delay’ from=’user1@my-jabber/Work’

stamp=’20021207T18:55:38’/>

</presence>

As you can see, this presence packet is sent unmodified to the remote s2s component.Again, we assume that the two s2s components are already connected and don’t have toauthenticate each other with the dialback protocol (described in Chapter 6).

The local client (user1@my-jabber) also wants to know the presence of the otherclient (user2@other-server), so JSM sends a presence probe packet also:

11) sessions → s2s

<presence type=’probe’ to=’user2@other-server’ from=’user1@my-jabber’/>

This packet is sent unmodified by the local s2s component to the s2s component onthe remote server:

12) Local s2s → Remote s2s

<presence type=’probe’ to=’user2@other-server’ from=’user1@my-jabber’/>

The remote s2s receives the packet, interacts with the remote JSM (not shown here),and responds with the presence status for user2@other-server:

06 0672325365 CH04 1/21/05 2:23 PM Page 140

Page 158: Jabber Developer’s Handbook [Sams 2004]

141Messages and Sessions

13) Remote s2s → Local s2s

<presence from=’user2@other-server/Work’ to=’user1@my-jabber’>

<status>Online</status>

<x xmlns=’jabber:x:delay’ from=’user2@other-server/Work’

stamp=’20021207T18:49:32’/>

</presence>

The local s2s accepts this packet and queues it for delivery to the JSM:

14) s2s → sessions

<presence from=’user2@other-server/Work’ to=’user1@my-jabber’>

<status>Online</status>

<x xmlns=’jabber:x:delay’ from=’user2@other-server/Work’

stamp=’20021207T18:49:32’/>

</presence>

The JSM wraps this presence packet in a <route> packet for delivery to the c2scomponent:

15) sessions → c2s

<route from=’user1@my-jabber/81F9BB0’ to=’16@c2s/813F5A0’>

<presence from=’user2@other-server/Work’ to=’user1@my-jabber’>

<status>Online</status>

<x xmlns=’jabber:x:delay’ from=’user2@other-server/Work’

stamp=’20021207T18:49:32’/>

</presence>

</route>

Finally, c2s can unwrap the <route> packet and deliver the presence information tothe client:

16) c2s → Client

<presence from=’user2@other-server/Work’ to=’user1@my-jabber’>

<status>Online</status>

<x xmlns=’jabber:x:delay’ from=’user2@other-server/Work’

stamp=’20021207T18:49:32’/>

</presence>

The sequence diagram in Figure 4.8 shows these messages from the perspective of thelocal client.

As you can see, presence turns out to be fairly complicated, requiring sixteen mes-sages among the client and server services just for a single (bi-directional) presence sub-scription. Of course, this is not counting those messages internal to the remote serverand between the remote server and its client.

06 0672325365 CH04 1/21/05 2:23 PM Page 141

Page 159: Jabber Developer’s Handbook [Sams 2004]

142 Chapter 4 Jabber Server Architecture

Figure 4.8 Registering presence.

Browsable AgentsIn step 3 (Querying For Agents) in the previous section, you saw that the instant messag-ing client queried the server for a list of agents when it connected.As described inChapter 2, this list is managed by the mod_agents module in the JSM.The list of brows-able agents is configured in the <browse> section of jabber.xml where a typical config-uration might look like this:

<browse>

<service type=”jud” jid=”jud.my-jabber” name=”Global Mega Corp Directory”>

<ns>jabber:iq:search</ns>

<ns>jabber:iq:register</ns>

</service>

<conference type=”public” jid=”conference.my-jabber”

name=”Public Chatrooms”/>

</browse>

The first entry (starting at the <service> tag) is for the Jabber User Directory.TheJID here refers to a service defined by a <service> section defined elsewhere in jabber.xml.The <ns> tags declare what XML namespaces the service understands. Inthis case, the jabber:iq:search tells clients that this service can be searched, and thejabber:iq:register namespace indicates that this service supports registration.The

�������������� �� �� ���� �� ������ ��

���������������������

��������������������

������������

��

��

��

��

��

06 0672325365 CH04 1/21/05 2:23 PM Page 142

Page 160: Jabber Developer’s Handbook [Sams 2004]

143Browsable Agents

corresponding service configuration appears separately in jabber.xml, outside the JSM (sessions) service configuration:

<service id=”jud”>

<host>jud.my-jabber</host>

<load><jud>./jud-0.4/jud.so</jud></load>

<jud xmlns=”jabber:config:jud”>

<vCard>

<FN>Global Mega Corp Directory</FN>

<DESC>This service provides a simple user directory service.</DESC>

<URL>http://foo.bar/</URL>

</vCard>

</jud>

</service>

Notice that the contents of the <host> tag (jud.my-jabber here) have to match theJID in the <browse> section of the JSM configuration.This is how clients know how tocontact services.

The second entry in the <browse> configuration above declares a special kind ofservice: a conference, or chat room, service. Its configuration is similar to the JUD serv-ice, and also appears separately as a <service> configuration:

<service id=’conference.my-jabber’>

<load><conference>./conference-0.4/conference.so</conference></load>

<host>conference.my-jabber</host>

<conference xmlns=”jabber:config:conference”>

<public/>

<vCard>

<FN>Public Chatrooms</FN>

<DESC>This service is for public chatrooms.</DESC>

<URL>http://foo.bar/</URL>

</vCard>

<history>20</history>

<notice>

<join> has become available</join>

<leave> has left</leave>

<rename> is now known as </rename>

</notice>

</conference>

</service>

Here again, the contents of the <host> tag (conference.my-jabber) match the jidattribute in the JSM’s <browse> configuration.

You saw earlier how a client queries the list of agents from the JSM; now look at howa client interacts with a service after the JSM tells it the list of services. By listing theservice in the <browse> section of the JSM configuration, you’re telling the client thatthis service responds to IQ messages in the jabber:iq:browse namespace.This meansthat the client can send a message to the JID for this service and get back informationabout it.To start the process, a client sends this IQ message to the JUD service:

06 0672325365 CH04 1/21/05 2:23 PM Page 143

Page 161: Jabber Developer’s Handbook [Sams 2004]

144 Chapter 4 Jabber Server Architecture

<iq type=”get” id=”browser_JCOM_4” to=”jud.my-jabber”>

<query xmlns=”jabber:iq:browse”/>

</iq>

The packet is routed to the jud.my-jabber component, which responds with a pack-et like this one:

<iq type=’result’ id=’browser_JCOM_4’ to=’user1@my-jabber/Work’

from=’jud.my-jabber’>

<service type=’jud’ xmlns=’jabber:iq:browse’

name=’Global Mega Corp Directory’>

<ns>jabber:iq:register</ns>

<ns>jabber:iq:search</ns>

</service>

</iq>

This is the same information that we put in the <browse> section of the JSM config-uration.They don’t have to be the same, but you can save clients a step if the JSM<browse> information is accurate.

This response says that this service supports registration using the jabber:iq:register namespace, so the client can register by first requesting the list of fields andthen generating a packet that sets those fields. First step: Send an IQ packet to get theregistration fields:

<iq type=”get” id=”JCOM_19” to=”jud.my-jabber”>

<query xmlns=”jabber:iq:register”/>

</iq>

Notice that the type=”get” attribute identifies this as a request for the registrationinstructions and not an actual registration.This message is routed to the JUD compo-nent, which replies with this information:

<iq type=’result’ id=’JCOM_20’ to=’user1@my-jabber/Work’ from=’jud.my-jabber’>

<query xmlns=’jabber:iq:register’>

<instructions>

Complete the form to submit your searchable attributes

in the Jabber User Directory

</instructions>

<name/>

<first/>

<last/>

<nick/>

<email/>

</query>

</iq>

This reply tells the client two things:The contents of the <instructions> elementgives the client human-readable instructions for the registration, and the other elementsinside the <query> element list the fields that are understood for registration. In thiscase, those fields are name, first, last, nick, and email.An instant messaging client can

06 0672325365 CH04 1/21/05 2:23 PM Page 144

Page 162: Jabber Developer’s Handbook [Sams 2004]

145Browsable Agents

Figure 4.9 JUD Registration Wizard.

Notice that the first panel in the wizard includes the text from the <instructions>element in the IQ packet.The second panel has fields for each one of the other ele-ments.When the user fills this in and submits it,WinJab sends this message:

<iq type=”set” id=”JCOM_26” to=”jud.my-jabber”>

<query xmlns=”jabber:iq:register”>

<name>Full Name</name>

<first>Full</first>

<last>Name</last>

<nick>user1</nick>

<email>user1@email</email>

</query>

</iq>

After it is inside the server, this packet is wrapped in a <route> packet as you sawearlier and routed to the JUD component. JUD parses the contents and sends an <xdb>message like this to store this information:

use this information to build a form or wizard to present to a user.The WinJab IM clientmakes a three-step wizard, as shown in Figure 4.9

06 0672325365 CH04 1/21/05 2:23 PM Page 145

Page 163: Jabber Developer’s Handbook [Sams 2004]

146 Chapter 4 Jabber Server Architecture

<xdb type=’set’ action=’insert’ match=’?jid=user1@my-jabber’

to=’jud’ from=’jud’ ns=’jabber:jud:users’ id=’2’>

<item jid=’user1@my-jabber’>

<name>Full Name</name>

<first>Full</first>

<last>Name</last>

<nick>user1</nick>

<email>user1@email</email>

</item>

</xdb>

xdb stores this data and replies back to JUD with the appropriate success packet.ThenJUD can reply to the client (by way of c2s, of course) with a success packet of its own:

<iq type=’result’ id=’JCOM_26’ to=’user1@my-jabber/Work’ from=’jud.my-jabber’/>

This lets the client know that its registration was accepted.Searching is a similar process. First, the client sends an IQ get packet by using the

jabber:iq:search namespace:

<iq to=”jud.my-jabber” type=”get” id=”wjSFields_wj_2”>

<query xmlns=”jabber:iq:search”/>

</iq>

This packet is routed to the JUD component, which responds with instructions and alist of fields that can be used to search for a record. It’s no surprise that these are thesame fields used to register:

<iq to=’user1@my-jabber/Work’ type=’result’ id=’wjSFields_wj_2’

from=’jud.my-jabber’>

<query xmlns=’jabber:iq:search’>

<instructions>

Fill in a field to search for any matching Jabber User

</instructions>

<name/>

<first/>

<last/>

<nick/>

<email/>

</query>

</iq>

WinJab uses this information to build the form shown in Figure 4.10.The instructions from the IQ response are at the top of the form and the fields are listedon the left side. If you fill in the form as shown, with the <first> field set to Full, andclick Search,WinJab sends this packet to JUD:

<iq to=”jud.my-jabber” id=”search_wj_6” type=”set”>

<query xmlns=”jabber:iq:search”>

<first>Full</first>

</query>

</iq>

06 0672325365 CH04 1/21/05 2:23 PM Page 146

Page 164: Jabber Developer’s Handbook [Sams 2004]

147Browsable Agents

Figure 4.10 JUD search form.

It’s interesting that the type of this packet is set even though we’re not “setting” any-thing. In the case of services like this, it may be clearer to think of get as a request forinstructions about how to use the service and set as an actual invocation of the service.

The packet gets routed to JUD in the usual way.You might expect it to get turnedinto a query to xdb (we sure did) but it turns out that this implementation of JUD storesall its data in memory and does one big fetch from JUD when it is first used. So theJUD component searches through its registrations and returns any that match in a packetlike this:

<iq to=’user1@my-jabber/Work’ id=’search_wj_13’ type=’result’

from=’jud.my-jabber’>

<query xmlns=’jabber:iq:search’>

<item jid=’user1@my-jabber’>

<name>Full Name</name>

<first>Full</first>

<last>Name</last>

<nick>user1</nick>

<email>user1@email</email>

</item>

</query>

</iq>

The <item> tag delimits separate entries in the JUD. In this case there is only oneentry.

We can think of the namespaces jabber:iq:search and jabber:iq:register asprotocols that both the JUD service and the WinJab client recognize.The powerful thingabout a protocol like this is that either end can be swapped out and the other end isnone the wiser.Any client that speaks the protocols can communicate with any servicethat speaks the protocols.The next chapters show you how to leverage these protocols.

Although these examples illustrate the use of the standard Jabber browsing, searching,and registering protocols, it’s also reasonable to define your own protocols by using yourown XML namespaces to distinguish them from the Jabber protocols. In this way, you’reusing the Jabber server architecture as a message routing processor between your ownservices and clients.The next chapter gives you examples of this, also.

06 0672325365 CH04 1/21/05 2:23 PM Page 147

Page 165: Jabber Developer’s Handbook [Sams 2004]

148 Chapter 4 Jabber Server Architecture

Instant Messaging GatewaysThe original motivation for Jabber was to build an extensible architecture that could beused to bridge between disparate instant messaging services.As you would expect, thesebridges are implemented as Jabber services.

At the time of this writing, several gateway transports are available onhttp://www.jabberstudio.org, including gateways for Yahoo Messenger,AOL InstantMessenger (AIM), Internet Relay Chat (IRC), and Microsoft’s MSN Messenger.They’reall similar in that they are Jabber services that know how to translate between the Jabberprotocols and the “foreign” IM system.They’re similar in principle, so in this section, welook closely at one of them: the AIM transport.

After downloading and building the AIM transport according to its instructions, youneed to configure the Jabber server to load the AIM transport service.You do that byadding a section like this to the services section of jabber.xml:

<service id=’aim.my-jabber’>

<load><aim_transport>./aim-transport/src/aimtrans.so</aim_transport></load>

<aimtrans xmlns=’jabber:config:aimtrans’>

<aimbinarydir>/usr/local/AIM</aimbinarydir>

<vCard>

<FN>AIM Transport</FN>

<DESC>An AIM Transport!</DESC>

<URL>http://foo.bar/</URL>

</vCard>

</aimtrans>

</service>

The <load> section tells the server that this will be a component loaded into theserver directly from the shared library listed here.The services configuration is in the<aimtrans> element and includes the odd entry <aimbinarydir>.The AIM transportservice uses information in AOL’s installation directory to talk to the server, so it requiresan installation of the AOL software.The last thing is the vCard, which just gives someinformation about this service.

You also want clients to be able to browse to this service, so you need to put an entryin the <browse> section of the JSM’s configuration. Something like this

<service type=”AIM (AOL Instant Messenger)” jid=”AIM (AOL Instant Messenger).my-➥ jabber” name=”AIM (AOL Instant Messenger) Transport”>

<ns>jabber:iq:gateway</ns>

<ns>jabber:iq:register</ns>

</service>

This entry tells clients that the service at JID, “AIM (AOL Instant Messenger).my-jabber”, is a gateway to another system (jabber:iq:gateway) and it supports regis-tration (jabber:iq:register), just as you saw earlier with the JUD component.

After these are added to jabber.xml and jabberd is restarted, the new service appearsto clients that query for agents, as in the WinJab browser shown in Figure 4.11.

06 0672325365 CH04 1/21/05 2:23 PM Page 148

Page 166: Jabber Developer’s Handbook [Sams 2004]

149Instant Messaging Gateways

Figure 4.11 Browsing the AIM Transport.

If you click on the AIM Transport icon,WinJab sends a browse IQ message to the AIMtransport service:

<iq type=”get” id=”browser_JCOM_64” to=”AIM (AOL Instant Messenger).my-jabber”>

<query xmlns=”jabber:iq:browse”/>

</iq>

As with the services you saw earlier, the AIM transport responds with its supportednamespaces:

<iq type=’result’ id=’browser_JCOM_64’ to=’user1@my-jabber/Work’

from=’aim.my-jabber’>

<service xmlns=’jabber:iq:browse’ type=’jabber’

jid=’aim.my-jabber’ name=’AIM Transport’>

<ns>jabber:iq:register</ns>

<ns>jabber:iq:gateway</ns>

</service>

</iq>

The <ns> elements contain the namespaces that the AIM transport understands. Let’sregister the AIM user with the gateway so it can translate for you.As you saw already, theclient can ask for the registration instructions with an IQ get packet like this:

<iq type=”get” id=”JCOM_65” to=”AIM (AOL Instant Messenger).my-jabber”>

<query xmlns=”jabber:iq:register”/>

</iq>

This packet gets routed to the AIM transport, which replies with its registrationinstructions:

<iq type=’result’ id=’JCOM_65’ to=’user1@my-jabber/Work’

from=’aim.my-jabber’>

06 0672325365 CH04 1/21/05 2:23 PM Page 149

Page 167: Jabber Developer’s Handbook [Sams 2004]

150 Chapter 4 Jabber Server Architecture

<query xmlns=’jabber:iq:register’>

<key>18b4718ea1ecb8fbff04a28cce188fb9d54760da</key>

<username/>

<password/>

<instructions>

Enter your AIM screenname or ICQ

UIN and the password for that account

</instructions>

</query>

</iq>

The gateway needs to know the AIM screenname and password (both user1-aim inthe following example packet) so it can log in to the AOL system.The client passes the<key> element back to the service when it does its registration:

<iq type=”set” id=”JCOM_67” to=”AIM (AOL Instant Messenger).my-jabber”>

<query xmlns=”jabber:iq:register”>

<username>user1-aim</username>

<password>user1-aim</password>

<key>18b4718ea1ecb8fbff04a28cce188fb9d54760da</key>

</query>

</iq>

If it succeeds in registering the AIM user, the gateway replies back with a successpacket like this:

<iq type=’result’ id=’JCOM_67’ to=’user1@my-jabber/Work’ from=’aim.my-jabber’/>

Now the service can relay messages between the AIM system and the Jabber server.Behind the scenes, the AIM transport has stored the registration information in xdb so itcan connect to the AIM system on your behalf.The gateway also needs to be able tosynchronize presence status on both systems, so it subscribes to user1’s presence by send-ing a presence packet like this:

<presence to=’user1@my-jabber/Work’ from=’aim.my-jabber/registered’

type=’subscribe’/>

Finally, the service advertises its own presence by sending its status in a presence packet:

<presence to=’user1@my-jabber/Work’ from=’aim.my-jabber’>

<status>Connected</status>

</presence>

Now that user1 is registered with the AIM transport, he can send a message to anAIM user called user2 by addressing it to the Jabber address [email protected] specification aim.my-jabber in the address causes this message to get routed insidethe Jabber server to the AIM gateway, which extracts the user name user2 and treats it asthe AIM screenname.

An AIM user can send user1 a message, too. Remember that the screenname user1-aim was used for registration with the AIM transport, so the gateway will pick up any AIM messages for user1-aim and route them through the Jabber server touser1@my-jabber.

06 0672325365 CH04 1/21/05 2:23 PM Page 150

Page 168: Jabber Developer’s Handbook [Sams 2004]

151Instant Messaging Gateways

Figure 4.12 shows how the gateway translates between Jabber and AIM addresses.TheJabber client (user1@my-jabber) addresses its message to the AIM user (screennameuser2-aim) as [email protected] Jabber gateway takes the Jabber user-name and translates that to an AIM screenname and passes the message to AIM.

AIM

Gatew

ay

AOLServer

JabberServer

Jabber Client(user1@my-jabber) AIM Client

(user2-aim)

Internet

To: [email protected]

To: user2-aim

To: user2-aim

Figure 4.12 Sending messages from Jabber to AIM.

Figure 4.13 shows the reverse. Here an AIM user (screenname user2-aim) addresses amessage to user1 by sending it to the screenname that user1 used when registering withthe AIM gateway (user1-aim).The AIM system delivers this message to the AIM gate-way, which converts it to a Jabber message addressed to user1@my-jabber for delivery asusual.

Addressing is probably the simplest example of the translations that a gateway has toperform.The gateway is also responsible for translating presence information betweensystems, as well as other features that may or may not be supported by the Jabber proto-cols. Because of these mismatches, most gateways support only a subset of the featuresavailable on foreign IM systems.

For the example illustrated in Figure 4.13,, the server to which the client was con-nected was the same one in which the gateway was installed.This is not a requirement atall.A client can register and use a service on any server that allows it access. Figure 4.14shows how this would work.

06 0672325365 CH04 1/21/05 2:23 PM Page 151

Page 169: Jabber Developer’s Handbook [Sams 2004]

152 Chapter 4 Jabber Server Architecture

Figure 4.13 Sending messages from AIM to Jabber.

s2sC

omponent

AOLServer

Jabber(server1)

s2sC

omponent

AIM

Gatew

ay

Jabber(server2)

Jabber Client(user1@server1) AIM Client

(user2-aim)

Internet Internet

To: [email protected]: user2-aim

To: user2-aim

To: [email protected]

AIM

Gatew

ay

AOLServer

JabberServer

Jabber Client(user1@my-jabber) AIM Client

(user2-aim)

Internet

To: user1@my-jabberTo: user1-aim

To: user1-aim

Figure 4.14 Sending messages through a remote gateway.

06 0672325365 CH04 1/21/05 2:24 PM Page 152

Page 170: Jabber Developer’s Handbook [Sams 2004]

153Summary

In this scenario, user1@server1 has registered with the AIM gateway on server2 (JID aim.server2). User1 can send its AIM message just as before and the s2s compo-nent on server1 routes it to server2, where it is routed to the AIM transport, which for-wards it to the AIM system.As you can see, it’s possible to set up a pretty elaborate set of forwarding agents.

SummaryIn this chapter, you’ve seen how messages flow among components in a typical Jabberserver and also how Jabber servers interact with one another.You saw some commoninstant messaging components, including the Jabber User Directory, the conferencingcomponent, and gateways.The next chapter expands on these concepts to show youhow you can use Jabber for applications other than instant messaging to do some incred-ibly flexible and scalable system-to-system integration.

06 0672325365 CH04 1/21/05 2:24 PM Page 153

Page 171: Jabber Developer’s Handbook [Sams 2004]

06 0672325365 CH04 1/21/05 2:24 PM Page 154

Page 172: Jabber Developer’s Handbook [Sams 2004]

5Extending the Jabber Server

In the service of Caesar, everything is legitimate.

Pierre Corneille (1606–1684), French playwright

All right, brain.You don’t like me and I don’t like you, but let’s just do this and I can get back tokilling you with beer.

Homer Simpson

THIS CHAPTER TAKES THE CONCEPTS THAT WERE covered somewhat abstractly in theprevious chapters and makes them concrete with some extended examples.You’ll seehow to use the components of the standard Jabber server in new ways and build compo-nents that give you capabilities beyond instant messaging. Indeed, you will see that justabout anything is legitimate in the realm of Jabber services.

As in previous chapters, this chapter is structured as a series of examples that illustratethe important concepts of building distributed applications with Jabber. In particular, theconcepts we’ll discuss include the following:

n Configuring the Jabber server to load a custom servicen Configuring the Jabber Session Manager (JSM) to advertise the capabilities of a

custom servicen Writing code to handle browse requests from clientsn Using the jabber:iq:search protocol to handle searchingn Using the jabber:iq:register protocol to register clients

07 0672325365 CH05 1/21/05 2:24 PM Page 155

Page 173: Jabber Developer’s Handbook [Sams 2004]

156 Extending the Jabber Server

n Building interfaces between Jabber services and external systems including rela-tional databases

n Using instant messaging clients to debug custom servicesn Sending error responses to clientsn Composing services (that is, services that call other services) into distributed appli-

cationsn Using the logging service to report status and errorsn Defining new XML namespace types and storing them in XDBn Interacting with Component Services

Although these topics are not exhaustive, by the end of this chapter, you’ll have a firmgrasp of what’s required to build a capable custom Jabber service and have a large bodyof code to draw from when you start to write your own service.

A Database ServiceThe first example shows how to use a Jabber service to make a relational database avail-able as a Jabber resource. Jabber concepts don’t map directly into the concepts of a rela-tional database, but there are some areas where they overlap:

n Searching. The Jabber protocols support searching with the jabber:iq:searchnamespace. Databases support searching with SQL. One can build a Jabber serviceto translate between these two languages so that a client can use the Jabber proto-col to search the database.

n Browsing. The Jabber JSM service supports browsing with the jabber:iq:browse namespace. One can build a Jabber service using the Jabber protocols thatenable a user to explore the database using an instant messaging client.

This example is in Java and uses the JabberBeans library to communicate with the server.It communicates to the database by using the Java JDBC libraries, which are part of thestandard distribution of the Java runtime environment.We use the MySQL database forthis example, but this service should be easily portable to any database with a JDBCdriver. Naturally, this Jabber service won’t make all the capabilities of SQL available toJabber clients, but it will show how any capabilities you might need could be realized asJabber services.

Configuring the Service

TIPThis service example assumes that you have installed and configured a MySQL database server and that you

have the Java connector for MySQL (called MySQL Connector/J) available. Instructions for downloading and

installing MySQL and the MySQL Java connector are at http://www.mysql.com/downloads/.

07 0672325365 CH05 1/21/05 2:24 PM Page 156

Page 174: Jabber Developer’s Handbook [Sams 2004]

157A Database Service

This service is built as an “accept” service, so it communicates with the server over aTCP/IP socket, which means that it doesn’t have to run on the same computer as theJabber server.This is probably the most flexible of the methods of connecting a Jabberservice in typical cases. Of the others, load requires the service to run on the same com-puter as the Jabber server itself, limiting scalability and flexibility.The connect methodrequires the service to accept the connection from the server, so it assumes that the serviceis always available—not as good for our purposes. In many cases where this service mightbe appropriate, the database might be behind a firewall while the Jabber server is outsidethe firewall so it can be publicly available.This configuration is shown in Figure 5.1.

Figure 5.1 A typical configuration of the database service.

In this configuration, because the database service makes the connection to the serverfrom inside the firewall, there’s no need to open any incoming ports on the firewall, sothere’s no firewall administration required and no additional security risks.

Here’s the stanza of XML that configures this service as an accept service in the jabber.xml file:

<service id=”dbservice.my-jabber”>

<accept>

<ip/>

<port>9001</port>

<secret>secret</secret>

</accept>

</service>

This says that the service will be called (its JID will be) dbservice.my-jabber (theserver’s name is my-jabber). It will connect to the server from an unknown IP address(<ip/>) on port 9001, using the password secret to prove its identity.

��������

�����

������

������

������

� ����

��������

����

�����

�� ������

�����

������

07 0672325365 CH05 1/21/05 2:24 PM Page 157

Page 175: Jabber Developer’s Handbook [Sams 2004]

158 Extending the Jabber Server

That’s all that is required for the service to be accepted and handled by the Jabberserver, but we also want the service to be available to clients for browsing and searchingusing the jabber:iq:browse and jabber:iq:search namespaces, so we need to config-ure the JSM to advertise the service.This is the stanza that, when added to the <browse>section of the JSM’s configuration, enables clients to discover the service:

<service type=”db” jid=”dbservice.my-jabber” name=”Database Service”>

<ns>jabber:iq:search</ns>

<ns>jabber:iq:browse</ns>

</service>

This says that the service named Database Service is available at the JID dbservice.my-jabber and that it responds to Jabber search and browse requests.Afterthese two stanzas are added to jabber.xml, the server is ready for the service to connect,and the JSM will advertise the service to browsing clients such as the instant messagingclient in Figure 5.2.

Figure 5.2 Browsing Jabber services.

Code ListingClicking on the Database Service icon in Figure 5.2 causes the IM client to send abrowse message to the service, so it’s time to look at the code.The JabberBeans library isa fairly low-level library, meaning that just about every element in an XML messagerequires a line of Java code, so this is a lengthy piece of code. Listing 5.1 provides it allhere; then we’ll go through it piece by piece.

Listing 5.1 The Database Service

1:package jdh.demos;

2:import java.net.*;

3:import java.io.*;

07 0672325365 CH05 1/21/05 2:24 PM Page 158

Page 176: Jabber Developer’s Handbook [Sams 2004]

159A Database Service

4:import java.util.*;

5:import java.sql.*;

6:import org.jabber.jabberbeans.*;

7:import org.jabber.jabberbeans.Extension.*;

8:import org.jabber.jabberbeans.serverside.*;

9:import org.jabber.jabberbeans.util.*;

10:public class DBService implements PacketListener {

11: ConnectionBean cb;

12: public DBService(String[] args) {

13: String server_host = args[0];

14: int port = Integer.parseInt(args[1]);

15: String secret = args[2];

16: cb = new ConnectionBean();

17: try {

18: cb.disableStreamHeader();

19: cb.connect(InetAddress.getByName(server_host), port);

20: setup(server_host, secret);

21: } catch (Exception ex) {

22: ex.printStackTrace();

23: }

24: }

25: // Initialize the connection to the jabber server.

26: private void setup(String server_host, String secret) throws Exception {

27: // load the JDBC driver

28: Class.forName(“org.gjt.mm.mysql.Driver”);

29: XMLStreamHeaderBuilder xsbuilder = new XMLStreamHeaderBuilder();

30: cb.addPacketListener(new PacketDebug());

31: SyncPacketListener sync = new SyncPacketListener(cb);

32: Packet p = null;

33: SHA1Helper sha = new SHA1Helper();

34: xsbuilder.setXMLNS(“jabber:component:accept”);

35: xsbuilder.setToAddress(new JID(server_host));

36: Packet xmlpacket = xsbuilder.build();

37: synchronized (sync) {

38: cb.send(xmlpacket);

39: p = sync.waitForType(xmlpacket, 5000);

40: }

41: if (p == null)

42: throw new RuntimeException(“unable to contact jabber server”);

43: String SessionID = cb.getSessionID();

44: Handshake handshake = new Handshake(sha.digest(SessionID, secret));

Listing 5.1 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 159

Page 177: Jabber Developer’s Handbook [Sams 2004]

160 Extending the Jabber Server

45: p = null;

46: sync.reset();

47: synchronized (sync) {

48: cb.send(handshake);

49: p = sync.waitForType(handshake, 5000);

50: }

51: if (p == null)

52: throw new RuntimeException(“unable to handshake with server”);

53: cb.addPacketListener(this);

54: }

55: // PacketListener interface. Called when any packet is received.

56: // Delegates IQ Browse and Search requests.

57: public void receivedPacket(PacketEvent packetEvent) {

58: Packet packet = packetEvent.getPacket();

59: if (packet instanceof InfoQuery) {

60: InfoQuery iq = (InfoQuery) packet;

61: try {

62: JID loc = iq.getToAddress();

63: Enumeration enum = iq.Extensions();

64: while (enum.hasMoreElements()) {

65: Extension ex = (Extension) enum.nextElement();

66: if (ex instanceof IQBrowse)

67: handleBrowse(iq, loc);

68: else if (ex instanceof IQSearchRequest)

69: handleSearch(iq, (IQSearchRequest) ex, loc);

70: continue;

71: }

72: } catch (Exception ex) {

73: ex.printStackTrace();

74: }

75: }

76: }

77: // PacketListener interface. Called when any packet is sent.

78: public void sentPacket(PacketEvent packetEvent) {}

79: // PacketListener interface. Called when a packet send fails.

80: public void sendFailed(PacketEvent packetEvent) {}

81: // Dispatch IQ browse requests based on the browse position

82: // (databases, tables, or fields)

83: private void handleBrowse(InfoQuery iq, JID to)

84: throws SQLException, InstantiationException {

85: String table = to.getResource();

86: String database = to.getUsername();

Listing 5.1 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 160

Page 178: Jabber Developer’s Handbook [Sams 2004]

161A Database Service

87: if (database == null) {

88: listDatabases(iq, to);

89: } else if (table == null) {

90: listTables(database, iq, to);

91: } else {

92: listFields(database, table, iq, to);

93: }

94: }

95: // Browse into the list of databases served by this service.

96: private void listDatabases(InfoQuery iq, JID to)

97: throws SQLException, InstantiationException {

98: Connection conn =

99: DriverManager.getConnection(“jdbc:mysql://localhost/mysql”);

100: DatabaseMetaData dmd = conn.getMetaData();

101: ResultSet rs = dmd.getCatalogs();

102: IQBrowseBuilder bb = new IQBrowseBuilder();

103: while (rs.next()) {

104: String dbname = rs.getString(1);

105: BrowseItemBuilder bib = new BrowseItemBuilder();

106: bib.setName(dbname);

107: bib.setCategory(“service”);

108: bib.setJID(new JID(dbname, to.getServer(), null));

109: bb.addChild(new BrowseItem(bib));

110: }

111: bb.addNameSpace(“jabber:iq:browse”);

112: bb.setIQ(true);

113: bb.setCategory(“service”);

114: bb.setType(“db”);

115: bb.setName(“Database Catalog”);

116: InfoQueryBuilder iqb = new InfoQueryBuilder();

117: iqb.addExtension(bb.build());

118: iqb.setFromAddress(iq.getToAddress());

119: iqb.setToAddress(iq.getFromAddress());

120: iqb.setType(“result”);

121: iqb.setIdentifier(iq.getIdentifier());

122: cb.send(iqb.build());

123: conn.close();

124: }

125: // Browse into the list of tables in a given database.

126: private void listTables(String database, InfoQuery iq, JID to)

127: throws SQLException, InstantiationException {

128: Connection conn =

129: DriverManager.getConnection(“jdbc:mysql://localhost/” + database);

Listing 5.1 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 161

Page 179: Jabber Developer’s Handbook [Sams 2004]

162 Extending the Jabber Server

130: DatabaseMetaData dmd = conn.getMetaData();

131: IQBrowseBuilder bb = new IQBrowseBuilder();

132: ResultSet rs = dmd.getTables(null, null, “%”, null);

133: while (rs.next()) {

134: String tablename = rs.getString(“TABLE_NAME”);

135: String tabletype = rs.getString(“TABLE_TYPE”);

136: BrowseItemBuilder bib = new BrowseItemBuilder();

137: bib.setName(tablename);

138: bib.setType(tabletype);

139: bib.setCategory(“service”);

140: bib.setIQ(true);

141: bib.setJID(new JID(database, to.getServer(), tablename));

142: bb.addChild(new BrowseItem(bib));

143: }

144: bb.setIQ(true);

145: bb.setCategory(“service”);

146: bb.addNameSpace(“jabber:iq:browse”);

147: bb.setType(“db”);

148: bb.setName(“Database “ + database);

149: InfoQueryBuilder iqb = new InfoQueryBuilder();

150: iqb.addExtension(bb.build());

151: iqb.setFromAddress(iq.getToAddress());

152: iqb.setToAddress(iq.getFromAddress());

153: iqb.setType(“result”);

154: iqb.setIdentifier(iq.getIdentifier());

155: cb.send(iqb.build());

156: conn.close();

157: }

158: // Browse into the list of fields for a given table

159: private void listFields(

160: String database,

161: String table,

162: InfoQuery iq,

163: JID to)

164: throws SQLException, InstantiationException {

165: Connection conn =

166: DriverManager.getConnection(“jdbc:mysql://localhost/” + database);

167: DatabaseMetaData dmd = conn.getMetaData();

168: IQBrowseBuilder bb = new IQBrowseBuilder();

169: ResultSet rs = dmd.getColumns(null, null, table, null);

170: while (rs.next()) {

171: String columnname = rs.getString(“COLUMN_NAME”);

172: String columntype = rs.getString(“TYPE_NAME”);

173: BrowseItemBuilder bib = new BrowseItemBuilder();

Listing 5.1 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 162

Page 180: Jabber Developer’s Handbook [Sams 2004]

163A Database Service

174: bib.setName(columnname);

175: bib.setType(columntype);

176: bib.setCategory(“item”);

177: bib.setJID(new JID(database, to.getServer(), table));

178: bb.addChild(new BrowseItem(bib));

179: }

180: bb.setIQ(true);

181: bb.addNameSpace(“jabber:iq:search”);

182: bb.setCategory(“service”);

183: bb.setType(“db”);

184: bb.setName(“Database “ + database + “/” + table);

185: InfoQueryBuilder iqb = new InfoQueryBuilder();

186: iqb.addExtension(bb.build());

187: iqb.setFromAddress(iq.getToAddress());

188: iqb.setToAddress(iq.getFromAddress());

189: iqb.setType(“result”);

190: iqb.setIdentifier(iq.getIdentifier());

191: cb.send(iqb.build());

192: conn.close();

193: }

194: // Dispatch IQ search requests. “get” to getFields(), “set” to search().

195: private void handleSearch(InfoQuery iq, IQSearchRequest ex, JID to)

196: throws SQLException, InstantiationException {

197: String table = to.getResource();

198: String database = to.getUsername();

199: if ((database == null) || (table == null)) {

200: sendError(iq);

201: } else if (iq.getType().equals(“get”)) {

202: getSearchInstructions(iq, ex, database, table);

203: } else if (iq.getType().equals(“set”)) {

204: search(iq, ex, database, table);

205: }

206: }

207: // Error reply to a malformed search request

208: private void sendError(InfoQuery iq) throws InstantiationException {

209: InfoQueryBuilder iqb = new InfoQueryBuilder();

210: iqb.setToAddress(iq.getFromAddress());

211: iqb.setFromAddress(iq.getToAddress());

212: iqb.setType(“error”);

213: iqb.setErrorCode(“400”);

214: iqb.setErrorText(“Bad Request”);

215: cb.send(iqb.build());

216: }

Listing 5.1 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 163

Page 181: Jabber Developer’s Handbook [Sams 2004]

164 Extending the Jabber Server

217: // Generate instructions for searching a table.

218: private void getSearchInstructions(

219: InfoQuery iq,

220: IQSearchRequest ex,

221: String database,

222: String table)

223: throws SQLException, InstantiationException {

224: Connection conn =

225: DriverManager.getConnection(“jdbc:mysql://localhost/” + database);

226: DatabaseMetaData dmd = conn.getMetaData();

227: IQSearchRequestBuilder iqsrb = new IQSearchRequestBuilder();

228: ResultSet rs = dmd.getColumns(null, null, table, null);

229: while (rs.next()) {

230: String columnname = rs.getString(“COLUMN_NAME”);

231: iqsrb.set(columnname, “”);

232: }

233: iqsrb.set(

234: “instructions”,

235: “Fill in search values for the table’s columns”);

236: InfoQueryBuilder iqb = new InfoQueryBuilder();

237: iqb.addExtension(iqsrb.build());

238: iqb.setFromAddress(iq.getToAddress());

239: iqb.setToAddress(iq.getFromAddress());

240: iqb.setType(“result”);

241: iqb.setIdentifier(iq.getIdentifier());

242: cb.send(iqb.build());

243: conn.close();

244: }

245: // Search a table based on the parameters of the search request.

246: private void search(

247: InfoQuery iq,

248: IQSearchRequest ex,

249: String database,

250: String table)

251: throws SQLException, InstantiationException {

252: Connection conn =

253: DriverManager.getConnection(“jdbc:mysql://localhost/” + database);

254: Statement stmt = conn.createStatement();

255: StringBuffer query = new StringBuffer(1024);

256: query.append(“select * from “);

257: query.append(table);

258: // Get the query params

259: Enumeration columns = ex.getNames();

260: boolean addedWhere = false;

Listing 5.1 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 164

Page 182: Jabber Developer’s Handbook [Sams 2004]

165A Database Service

261: while (columns.hasMoreElements()) {

262: String column = (String) columns.nextElement();

263: String value = ex.getValue(column);

264: if ((value == null) || value.equals(“”))

265: continue; // field was left blank

266: if (!addedWhere) {

267: addedWhere = true;

268: query.append(“ where “ + column + “ like \’” + value + “\’”);

269: } else {

270: query.append(“ and “ + column + “ like \’” + value + “\’”);

271: }

272: }

273: ResultSet rs = stmt.executeQuery(query.toString());

274: ResultSetMetaData rsmd = rs.getMetaData();

275: int columnCount = rsmd.getColumnCount();

276: IQSearchResultBuilder iqsrb = new IQSearchResultBuilder();

277: while (rs.next()) {

278: SearchResultBuilder srb = new SearchResultBuilder();

279: for (int col = 1; col <= columnCount; col++) {

280: String label = rsmd.getColumnLabel(col);

281: String value = rs.getString(col);

282: srb.set(label, value == null ? “(null)” : value);

283: }

284: iqsrb.addSearchResult((SearchResult) srb.build());

285: }

286: InfoQueryBuilder iqb = new InfoQueryBuilder();

287: iqb.addExtension(iqsrb.build());

288: iqb.setFromAddress(iq.getToAddress());

289: iqb.setToAddress(iq.getFromAddress());

290: iqb.setType(“result”);

291: iqb.setIdentifier(iq.getIdentifier());

292: cb.send(iqb.build());

293: conn.close();

294: }

295: // This is where it all starts

296: public static void main(String[] args) {

297: new DBService(args);

298: }

299:}

That’s a lot of code to take in all at once. Let’s examine these (almost) 300 lines of codea piece at a time.

Listing 5.1 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 165

Page 183: Jabber Developer’s Handbook [Sams 2004]

166 Extending the Jabber Server

Connecting to the ServerThe first thing the service needs to do (besides startup housekeeping such as parsing thecommand-line arguments) is connect to the Jabber server.This is started in the construc-tor and finished in the setup() method.These lines in the constructor make the actualTCP/IP connection to the server:

18: cb.disableStreamHeader();

19: cb.connect(InetAddress.getByName(server_host), port);

Line 18 disables the automatic generation of the stream header that the JabberBeanslibrary would do automatically if this were a client. Because this is a service, we need tohandle in manually and we do that in the setup() method that follows.

Listing 5.2 Establishing the Connection with the Server

26: private void setup(String server_host, String secret) throws Exception {

27: // load the JDBC driver

28: Class.forName(“org.gjt.mm.mysql.Driver”);

29: XMLStreamHeaderBuilder xsbuilder = new XMLStreamHeaderBuilder();

30: cb.addPacketListener(new PacketDebug());

31: SyncPacketListener sync = new SyncPacketListener(cb);

32: Packet p = null;

33: SHA1Helper sha = new SHA1Helper();

34: xsbuilder.setXMLNS(“jabber:component:accept”);

35: xsbuilder.setToAddress(new JID(server_host));

36: Packet xmlpacket = xsbuilder.build();

37: synchronized (sync) {

38: cb.send(xmlpacket);

39: p = sync.waitForType(xmlpacket, 5000);

40: }

41: if (p == null)

42: throw new RuntimeException(“unable to contact jabber server”);

43: String SessionID = cb.getSessionID();

44: Handshake handshake = new Handshake(sha.digest(SessionID, secret));

45: p = null;

46: sync.reset();

47: synchronized (sync) {

48: cb.send(handshake);

49: p = sync.waitForType(handshake, 5000);

50: }

51: if (p == null)

52: throw new RuntimeException(“unable to handshake with server”);

53: cb.addPacketListener(this);

54: }

07 0672325365 CH05 1/21/05 2:24 PM Page 166

Page 184: Jabber Developer’s Handbook [Sams 2004]

167A Database Service

The first thing you’ll notice about this method is the odd call to Class.forName() atline 28—it throws away the return value.This just loads the JDBC driver class for theMySQL and has the side effect of registering the driver so we can use it later to com-municate with the database.The basics of JDBC are covered in the next section.

At this point, the service is connected to the server, but it hasn’t sent a single byte. Itneeds to establish the XML stream with the server, so the next thing this method does iscreate the stream header (lines 29–36) and send it to the server.We use a JabberBeans syn-chronous listener so we can wait for the response rather than receive a callback methodinvocation (lines 37–40).The XML stream header sent by this service looks like this:

<stream:stream xmlns:stream=”http://etherx.jabber.org/streams”

xmlns=”jabber:component:accept” to=”my-jabber”>

The server responds with its own stream header, which includes the JID in the fromattribute:

<stream:stream xmlns:stream=”http://etherx.jabber.org/streams”

xmlns=”jabber:component:accept” from=”dbservice.my-jabber” id=”3E0C70CA”>

The server’s stream header also includes the session ID, which we’ll use with a secretkey (secret) to verify to the server that this is the real database service.We make a newHandshake class using the key and the session ID and send it to the server (lines 44–52).If the server doesn’t hang up on us (close the socket), the handshake was successful andwe can add the service as a listener for packets (line 53).

NOTESee Chapter 6, “Jabber Security,” for more details on the component handshake protocol.

JDBC Survival SkillsThe Java JDBC classes provide everything you need to access data stores that look likerelational databases. For this example, we need only a small fraction of that capability, sothis is a brief and limited introduction.

A JDBC connection to a database is made by using the staticDriverManager.getConnection() method, which takes a string URL parameter. Forexample

jdbc:mysql://localhost/mydatabase

This URL sys that this is a JDBC URL (jdbc), specifies the type of database (mysql),the name of the computer on which to connect to the database (localhost), and thename of the database to connect to (mydatabase).This URL appears several times in thisexample, so if you change it be sure to change it everywhere.

07 0672325365 CH05 1/21/05 2:24 PM Page 167

Page 185: Jabber Developer’s Handbook [Sams 2004]

168 Extending the Jabber Server

TIPIf you configure your MySQL database to require a username and password for access, you can either add

these to the URL (for example, jdbc:mysql://localhost/mysql?user=foo&password=bar),

or use the version of the JDBC API for DriverManager.getConnection() that accepts the user-

name and password as arguments.

Each host can manage multiple databases, so the JDBC connection can be used toquery for the catalog of available databases.This line of Java code gets the list of availabledatabases from the JDBC Connection (conn) and stores it in the JDBC ResultSetobject:

ResultSet rs = conn.getMetaData().getCatalogs();

The ResultSet is much like a Java iterator in that it has a next() method thatadvances the iterator to the next entry in the list. Each element in the ResultSet objectcan have several fields (columns) that can be of various types, so typical processing of aResultSet looks something like this:

while (rs.next()) {

String dbname = rs.getString(1);

int num = rs.getInt(2);

Date d = rs.getDate(3);

}

Yes, the field (column) numbers start with 1 and not 0 as they should.You can alsouse the string names of the columns rather than numbers to read the ResultSet.Thedatabase service example uses both column numbers and column names.

After we get the name of the database by using the getCatalogs() method, we cancreate a JDBC connection to it by putting its name after the / in the JDBC URL. Eachdatabase contains some number of tables and we can read the list of tables in the data-base by using the getTables() method of the DatabaseMetaData class, like this:

ResultSet rs = conn.getMetaData().dmd.getTables(null, null, “%”, null);

The various parameters to getTables() enable us to filter the tables by type andname, but this statement just gets all tables regardless of name or type.

Now that we know the name of the table, we can use SQL to query its contents.SQL is a large and sophisticated language, but we need to only scratch the surface andquery for all columns in a single table that match some criteria. Here’s an example ofthis kind of SQL statement:

select * from table1 where field1 like ‘foo%’ and field2 like ‘??bar’

This statement selects all the columns from the table named table in which the col-umn named field1 contains a value that starts with the string foo and the columnnamed field2 contains a value that ends with bar, preceded by any two characters.

There is a lot more to JDBC and SQL, but that much should get you started. Nextyou’ll see how you can make JDBC and SQL useful to Jabber clients.

07 0672325365 CH05 1/21/05 2:24 PM Page 168

Page 186: Jabber Developer’s Handbook [Sams 2004]

169A Database Service

BrowsingThere are three levels to browsing a JDBC database: databases, tables, and fields (oftencalled columns), as shown in Figure 5.3.

Figure 5.3 Browsing levels of a database.

The service encodes the levels of the database in the JID that it gives to a client when itbrowses into the next level.You already saw that the JSM advertises the database servicebased on the information in its <browse> configuration, but after the client browses intothat service, the JSM doesn’t handle the IQ messages—they go to the service itself.Thedatabase service’s receivePacket() method dispatches the IQ browse messages to thehandleBrowse() method, which you can see repeated in Listing 5.3.

Listing 5.3 Handling Browse Requests

83: private void handleBrowse(InfoQuery iq, JID to)

84: throws SQLException, InstantiationException {

85: String table = to.getResource();

86: String database = to.getUsername();

87: if (database == null) {

88: listDatabases(iq, to);

89: } else if (table == null) {

90: listTables(database, iq, to);

91: } else {

92: listFields(database, table, iq, to);

93: }

94: }

�������� �

��������

�����

�������� �������� �

����� ����� ����� ����� ����� �����

������

������

������

������

������

������

������

������

������

������

������

������

������

������

������

������

������

������

������

������

������

������

������

������

07 0672325365 CH05 1/21/05 2:24 PM Page 169

Page 187: Jabber Developer’s Handbook [Sams 2004]

170 Extending the Jabber Server

This method parses the JID to see what the requestor is trying to browse. If the JIDcontains just the host (for example, dbservice.my-jabber), then the client is browsingat the top level, and we respond with the list of databases (line 88). If the JID containsthe username (for example, [email protected]), we interpret that as thename of the database and respond with a list of tables in that database (line 90). If all three parts of the JID are present (for example, [email protected]/tablename), we interpret the resource as the name of the table to be browsed andrespond with a list of the fields in that table.

This is an example of an IQ browse request that a client would send at the top levelof the browse tree:

<iq to=”dbservice.my-jabber” from=”user1@my-jabber/Exodus”

id=”jcl_31” type=”get”>

<query xmlns=”jabber:iq:browse”></query>

</iq>

Notice that the to JID attribute contains just the hostname—no username orresource.The database service responds with a list of the three databases in the catalog:

<iq to=”user1@my-jabber/Exodus” from=”dbservice.my-jabber”

id=”jcl_31” type=”result”>

<service xmlns=”jabber:iq:browse” type=”db” name=”Database Catalog”>

<ns>jabber:iq:browse</ns>

<service name=”mysql” jid=”[email protected]”></service>

<service name=”rawdata” jid=”[email protected]”></service>

<service name=”reports” jid=”[email protected]”></service>

</service>

</iq>

In the response, the JIDs on the subelements include the username portion, so furtherbrowsing will include that information. Figure 5.4 shows how the IM client presents thisresponse.

Figure 5.4 Browsing the top of the database.

07 0672325365 CH05 1/21/05 2:24 PM Page 170

Page 188: Jabber Developer’s Handbook [Sams 2004]

171A Database Service

The code that generates this response is in the listDatabases() method in Listing 5.4.

Listing 5.4 Listing the Databases

96: private void listDatabases(InfoQuery iq, JID to)

97: throws SQLException, InstantiationException {

98: Connection conn =

99: DriverManager.getConnection(“jdbc:mysql://localhost/mysql”);

100: DatabaseMetaData dmd = conn.getMetaData();

101: ResultSet rs = dmd.getCatalogs();

102: IQBrowseBuilder bb = new IQBrowseBuilder();

103: while (rs.next()) {

104: String dbname = rs.getString(1);

105: BrowseItemBuilder bib = new BrowseItemBuilder();

106: bib.setName(dbname);

107: bib.setCategory(“service”);

108: bib.setJID(new JID(dbname, to.getServer(), null));

109: bb.addChild(new BrowseItem(bib));

110: }

111: bb.addNameSpace(“jabber:iq:browse”);

112: bb.setIQ(true);

113: bb.setCategory(“service”);

114: bb.setType(“db”);

115: bb.setName(“Database Catalog”);

116: InfoQueryBuilder iqb = new InfoQueryBuilder();

117: iqb.addExtension(bb.build());

118: iqb.setFromAddress(iq.getToAddress());

119: iqb.setToAddress(iq.getFromAddress());

120: iqb.setType(“result”);

121: iqb.setIdentifier(iq.getIdentifier());

122: cb.send(iqb.build());

123: conn.close();

124: }

This method gets the list of databases from the MySQL administration database (lines98–101) and uses the JabberBeans IQBrowseBuilder (line 102) to build a browseresponse. For each of the databases in the catalog, it builds a <service> element in thebrowse response and sets the username of its JID to the name of the database (line 108). Italso sets the name to be displayed in the IM client to the name of the database (line 106).We add the jabber:iq:browse namespace to indicate that this level can also be browsed(line 111), finish assembling and addressing the IQ response, and send it (lines 112–122).

When the client browses into one of the databases, the JID username tells the servicewhich database is being browsed. Here the client is browsing the rawdata database:

<iq to=”[email protected]” from=”user1@my-jabber/Exodus”

id=”jcl_32” type=”get”>

<query xmlns=”jabber:iq:browse”></query>

</iq>

07 0672325365 CH05 1/21/05 2:24 PM Page 171

Page 189: Jabber Developer’s Handbook [Sams 2004]

172 Extending the Jabber Server

The database service responds to this packet with the list of tables in that database.Each JID in the elements of this response includes a username (specifying the database)and a resource (specifying a table).

<iq to=”user1@my-jabber/Exodus” from=”[email protected]

id=”jcl_32” type=”result”>

<service xmlns=”jabber:iq:browse” type=”db” name=”Database rawdata”>

<ns>jabber:iq:browse</ns>

<service type=”TABLE” name=”inventory”

jid=”[email protected]/inventory”>

</service>

<service type=”TABLE” name=”invoices”

jid=”[email protected]/invoices”>

</service>

<service type=”TABLE” name=”phonebook”

jid=”[email protected]/phonebook”>

</service>

</service>

</iq>

Figure 5.5 shows how the Exodus client displays the results. Notice that the JID ofthe element being browsed is shown in the “Jabber Address” bar and the name we gavethis element (“Database rawdata”) is shown in the left pane.

Figure 5.5 Browsing the tables of a database.

The code that generates this response is in the method listTables() (lines 126–157)shown earlier in Listing 5.1. It’s so similar to listDatabases() that we won’t repeat ithere. (In fact, some clever refactoring could make them use much the same code, but atthe cost of some clarity for instructional purposes.)

Browsing into the next level brings the client the list of columns in the selected tableof the selected database.The JID of the to attribute indicates that this is a request tobrowse into the inventory table of the rawdata database:

07 0672325365 CH05 1/21/05 2:24 PM Page 172

Page 190: Jabber Developer’s Handbook [Sams 2004]

173A Database Service

<iq to=”[email protected]/inventory” from=”user1@my-jabber/Exodus”

id=”jcl_41” type=”get”>

<query xmlns=”jabber:iq:browse”></query>

</iq>

The database service responds to this packet with a list of the columns in that table:

<iq to=”user1@my-jabber/Exodus” from=”[email protected]/inventory”

id=”jcl_41” type=”result”>

<service xmlns=”jabber:iq:browse” type=”db”

name=”Database rawdata/inventory”>

<ns>jabber:iq:search</ns>

<item type=”varchar” name=”ITEM”

jid=”[email protected]/inventory”></item>

<item type=”decimal” name=”COST”

jid=”[email protected]/inventory”></item>

<item type=”int” name=”ONHAND”

jid=”[email protected]/inventory”></item>

</service>

</iq>

The three columns in this table are ITEM, COST, and ONHAND. Because this is the lowestlevel of the browse hierarchy, this response is a little different from the others.The XML namespace in the <ns> element is no longer jabber:iq:browse but rather jabber:iq:search, indicating that this element can be searched (more about that later).Also rather than including <service> XML elements, we use <item> elements.Thiscauses the IM client to display a different icon, as shown in Figure 5.6.The response alsoincludes the data type of the column, so the IM client can display that, too.

TIPAlthough this example uses the username for one level of browsing and the resource for the other, you’re not

limited to two levels with this technique. It’s easy to encode any number of levels in the resource part of the

JID by using some separator that you can distinguish from the names of the levels. For example, you could

use commas to separate levels, resulting in a JID like name@service/level1,level2,level3 to

represent browsing three levels down.

Again the code for the listFields() method is very similar to the others, so we won’trepeat it here. One important difference is the change in what JabberBeans calls theCategoryType to item rather than service (line 176), which changes the XML tag,which cues the IM client to display the different icon.Another is the inclusion of thejabber:iq:search namespace (line 181) rather than the jabber:iq:browse namespace.

Notice the new link in the left pane in Figure 5.6: Search Using This Service orAgent. Clicking on this link invokes the search capability that the database service adver-tised by including the jabber:iq:search namespace in its response.

07 0672325365 CH05 1/21/05 2:24 PM Page 173

Page 191: Jabber Developer’s Handbook [Sams 2004]

174 Extending the Jabber Server

Figure 5.6 Browsing to the fields of a table.

SearchingSearching is the other important capability implemented by the database service.We’veseen a hint of the searching function in the previous section on browsing and it’s time toget into the details.When the IM user clicks on the Search Using This Service or Agentlink, Exodus sends this packet to the database service:

<iq to=”[email protected]/inventory” from=”user1@my-jabber/Exodus”

id=”jcl_44” type=”get”>

<query xmlns=”jabber:iq:search”></query>

</iq>

This looks almost exactly like a browse request except for the XML namespaceattribute within the <query> element.The namespace jabber:iq:search is all thatidentifies this as a search request rather than a browse request.This packet’s type is get,which means that it is a request for instructions and a list of the fields that can be usedfor searching.

JabberBeans converts this packet into an IQSearchRequest object, whichreceivedPacket() dispatches to the handleSearch() method repeated in Listing 5.5for convenience.

Listing 5.5 Dispatching Search Requests

195: private void handleSearch(InfoQuery iq, IQSearchRequest ex, JID to)

196: throws SQLException, InstantiationException {

197: String table = to.getResource();

198: String database = to.getUsername();

199: if ((database == null) || (table == null)) {

200: sendError(iq);

201: } else if (iq.getType().equals(“get”)) {

202: getSearchInstructions(iq, ex, database, table);

07 0672325365 CH05 1/21/05 2:24 PM Page 174

Page 192: Jabber Developer’s Handbook [Sams 2004]

175A Database Service

203: } else if (iq.getType().equals(“set”)) {

204: search(iq, ex, database, table);

205: }

206: }

The first thing handleSearch() does is parse the JID to make sure a database andtable are specified in the username and resource fields of the JID. If either one is notpresent, something is wrong and the service responds with an error by calling thesendError() method (lines 208–216). If the database service sets up the JIDs of thebrowse items correctly, the Exodus IM client doesn’t generate a packet with this error,but we can generate one manually to see what it does. If we send this packet (which hasno resource in the “to” address),

<iq id=”jcl_44” to=”[email protected]” type=”get”>

<query xmlns=”jabber:iq:search”/>

</iq>

the database service responds with an IQ error packet like this:

<iq to=’user1@my-jabber/Exodus’ from=’[email protected]

id=’sn-0’ type=’error’>

<error code=’400’>Bad Request</error>

</iq>

If the JID is well-formed, the service responds with the instructions by reading themetadata of the requested table in the getSearchInstructions() method, as shown inListing 5.6.

Listing 5.6 Generating Search Instructions

218: private void getSearchInstructions(

219: InfoQuery iq,

220: IQSearchRequest ex,

221: String database,

222: String table)

223: throws SQLException, InstantiationException {

224: Connection conn =

225: DriverManager.getConnection(“jdbc:mysql://localhost/” + database);

226: DatabaseMetaData dmd = conn.getMetaData();

227: IQSearchRequestBuilder iqsrb = new IQSearchRequestBuilder();

228: ResultSet rs = dmd.getColumns(null, null, table, null);

229: while (rs.next()) {

230: String columnname = rs.getString(“COLUMN_NAME”);

231: iqsrb.set(columnname, “”);

232: }

233: iqsrb.set(

Listing 5.5 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 175

Page 193: Jabber Developer’s Handbook [Sams 2004]

176 Extending the Jabber Server

234: “instructions”,

235: “Fill in search values for the table’s columns”);

236: InfoQueryBuilder iqb = new InfoQueryBuilder();

237: iqb.addExtension(iqsrb.build());

238: iqb.setFromAddress(iq.getToAddress());

239: iqb.setToAddress(iq.getFromAddress());

240: iqb.setType(“result”);

241: iqb.setIdentifier(iq.getIdentifier());

242: cb.send(iqb.build());

243: conn.close();

244: }

The JDBC DatabaseMetaData object’s getColumns() method (line 226) returns the listof columns in this table.We pull out the name of each column and add an empty ele-ment for each one to the search request (lines 230–231).The presence of the empty ele-ment tells the client that there is a field by that name available for searching. One specialelement, called <instructions>, contains the text that should be displayed to the user.Finally, the response packet is addressed and sent (lines 236–242) and the result is thispacket:

<iq to=”user1@my-jabber/Exodus” from=”[email protected]/inventory”

id=”jcl_44” type=”result”>

<query xmlns=”jabber:iq:search”>

<instructions>

Fill in search values for the table&apos;s columns

</instructions>

<ONHAND/>

<ITEM/>

<COST/>

</query>

</iq>

This packet tells the client that this table has three fields that can be searched: ONHAND,ITEM, and COST.The client can use this information to build a form containing appropri-ate text boxes, like the one the WinJab client builds in Figure 5.7.

This form includes the instructions in the upper left corner and text boxes for eachof the three fields in the left pane.

NOTEThe instructions in the example (lines 233–235) included an apostrophe (table’s), which would break the

XML parsing if it hadn’t been automatically escaped for us (table&apos;s), to comply with the rules for

well-formed XML. The IM client un-escapes it before displaying it.

Listing 5.6 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 176

Page 194: Jabber Developer’s Handbook [Sams 2004]

177A Database Service

Figure 5.7 Search form built by WinJab, based on the IQ get response.

If we fill in the form with a value for NAME of %Sprocket (% is the SQL wildcard charac-ter) and click Search, the IM client generates this packet for the database service:

<iq to=”[email protected]/inventory” from=”user1@my-jabber/Exodus”

id=”search_wj_5” type=”set”>

<query xmlns=”jabber:iq:search”>

<NAME>%Sprocket</NAME>

</query>

</iq>

Because the type attribute of this packet is set, the database service interprets it as arequest to do a search and delegates it to the search() method shown in Listing 5.7.

First, it starts building a SQL statement in a string buffer (lines 256–257), then itappends any where clauses (lines 268 and 270) based on the contents of the search pack-et.The query built for the previous iq packet would be select * from inventorywhere NAME like ‘%Sprocket’.

Listing 5.7 Searching the Database

246: private void search(

247: InfoQuery iq,

248: IQSearchRequest ex,

249: String database,

250: String table)

251: throws SQLException, InstantiationException {

252: Connection conn =

253: DriverManager.getConnection(“jdbc:mysql://localhost/” + database);

254: Statement stmt = conn.createStatement();

255: StringBuffer query = new StringBuffer(1024);

256: query.append(“select * from “);

257: query.append(table);

258: // Get the query params

259: Enumeration columns = ex.getNames();

260: boolean addedWhere = false;

261: while (columns.hasMoreElements()) {

07 0672325365 CH05 1/21/05 2:24 PM Page 177

Page 195: Jabber Developer’s Handbook [Sams 2004]

178 Extending the Jabber Server

262: String column = (String) columns.nextElement();

263: String value = ex.getValue(column);

264: if ((value == null) || value.equals(“”))

265: continue; // field was left blank

266: if (!addedWhere) {

267: addedWhere = true;

268: query.append(“ where “ + column + “ like \’” + value + “\’”);

269: } else {

270: query.append(“ and “ + column + “ like \’” + value + “\’”);

271: }

272: }

273: ResultSet rs = stmt.executeQuery(query.toString());

274: ResultSetMetaData rsmd = rs.getMetaData();

275: int columnCount = rsmd.getColumnCount();

276: IQSearchResultBuilder iqsrb = new IQSearchResultBuilder();

277: while (rs.next()) {

278: SearchResultBuilder srb = new SearchResultBuilder();

279: for (int col = 1; col <= columnCount; col++) {

280: String label = rsmd.getColumnLabel(col);

281: String value = rs.getString(col);

282: srb.set(label, value == null ? “(null)” : value);

283: }

284: iqsrb.addSearchResult((SearchResult) srb.build());

285: }

286: InfoQueryBuilder iqb = new InfoQueryBuilder();

287: iqb.addExtension(iqsrb.build());

288: iqb.setFromAddress(iq.getToAddress());

289: iqb.setToAddress(iq.getFromAddress());

290: iqb.setType(“result”);

291: iqb.setIdentifier(iq.getIdentifier());

292: cb.send(iqb.build());

293: conn.close();

294: }

The query is finally executed against the database at line 273 and the resultset is put intoa search result packet in lines 277–285. Here we have to use the resultset’s metadata toget the names of the columns so we can fill in the element tags for the search result.Thepacket that is sent back to the client (line 292) looks like this:

<iq to=”user1@my-jabber/Exodus” from=”[email protected]/inventory”

id=”search_wj_5” type=”result”>

<query xmlns=”jabber:iq:search”>

<item>

<COST>1.95</COST>

<NAME>Sprocket</NAME>

Listing 5.7 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 178

Page 196: Jabber Developer’s Handbook [Sams 2004]

179A Report Service

<ONHAND>1500</ONHAND>

</item>

<item>

<COST>44.95</COST>

<NAME>Big Sprocket</NAME>

<ONHAND>543</ONHAND>

</item>

<item>

<COST>18.95</COST>

<NAME>Green Sprocket</NAME>

<ONHAND>495</ONHAND>

</item>

<item>

<COST>199.95</COST>

<NAME>Wooden Sprocket</NAME>

<ONHAND>446</ONHAND>

</item>

</query>

</iq>

From this, you can see that the query matched four rows in the database table asdelimited by the <item> tags. Figure 5.8 shows how the IM client displays this data.

Figure 5.8 Search results as displayed by the IM client.

The title bar and the Add buttons in Figure 5.8 show that this IM client was intended tosearch the Jabber User Directory (JUD) for contacts to add to its roster.As you’ve seen, theJabber service searching mechanism is more general and can be put to many other uses.

A Report ServiceThe next service example builds on the previous example in that it acts as a client of thedatabase service to generate reports.These reports are pretty simple (actually, just the fullcontents of a table), but because you just saw how to do searching on the database serv-ice in the previous example, there’s no need to repeat it here.

07 0672325365 CH05 1/21/05 2:24 PM Page 179

Page 197: Jabber Developer’s Handbook [Sams 2004]

180 Extending the Jabber Server

This service accepts registration requests from clients and delivers the reports request-ed in that registration to those clients. It uses a Java timer to generate daily reports, butalso can generate reports on demand.This is especially useful for debugging becausewaiting a whole day for the next set of reports is not very productive!

This example also illustrates how to use the XDB service to store and retrieve dataand how to define a new XML namespace and have it recognized by the JabberBeanslibrary.

Code ListingListing 5.8 is the code for the reports service.The initialization and connection to theserver is almost identical to the database service, so we won’t dwell on it here. Just a cou-ple things to point out: Notice line 9, where we hardcode the database service’s JID. Itwould be a nice enhancement to find the service JID by browsing the JSM.Also, noticeline 44; here we extract the JID of the reports service from the XML stream header thatthe server sends.We didn’t need this in the database service because the only time itgenerates messages is in response to incoming messages, so it could just copy the “to”address of the incoming message to the “from” address of its response.This service gener-ates messages on its own, so it needs to know its address explicitly.

Listing 5.8 The Reports Service

1:package jdh.demos;

2:import java.net.*;

3:import java.util.*;

4:import org.jabber.jabberbeans.*;

5:import org.jabber.jabberbeans.Extension.*;

6:import org.jabber.jabberbeans.serverside.*;

7:import org.jabber.jabberbeans.util.*;

8:public class ReportService implements PacketListener {

9: private String dbService = “[email protected]/”;

10: ConnectionBean cb;

11: private Hashtable cache = new Hashtable();

12: private JID myJid;

13: public ReportService(String[] args) {

14: String server_host = args[0];

15: int port = Integer.parseInt(args[1]);

16: String secret = args[2];

17: cb = new ConnectionBean();

18: try {

19: cb.disableStreamHeader();

20: cb.connect(InetAddress.getByName(server_host), port);

21: setup(server_host, secret);

22: } catch (Exception ex) {

07 0672325365 CH05 1/21/05 2:24 PM Page 180

Page 198: Jabber Developer’s Handbook [Sams 2004]

181A Report Service

23: ex.printStackTrace();

24: }

25: }

26: // Initialize the connection to the jabber server.

27: private void setup(String server_host, String secret) throws Exception {

28: XMLStreamHeaderBuilder xsbuilder = new XMLStreamHeaderBuilder();

29: cb.addPacketListener(new PacketDebug());

30: SyncPacketListener sync = new SyncPacketListener(cb);

31: Packet p;

32: SHA1Helper sha;

33: sha = new SHA1Helper();

34: xsbuilder.setXMLNS(“jabber:component:accept”);

35: xsbuilder.setToAddress(new JID(server_host));

36: Packet xmlpacket = xsbuilder.build();

37: p = null;

38: synchronized (sync) {

39: cb.send(xmlpacket);

40: p = sync.waitForType(xmlpacket, 5000);

41: }

42: if (p == null)

43: throw new RuntimeException(“unable to contact jabber server”);

44: myJid = ((XMLStreamHeader) p).getFromAddress();

45: String SessionID = cb.getSessionID();

46: Handshake handshake = new Handshake(sha.digest(SessionID, secret));

47: p = null;

48: sync.reset();

49: p = null;

50: synchronized (sync) {

51: cb.send(handshake);

52: p = sync.waitForType(handshake, 5000);

53: }

54: if (p == null)

55: throw new RuntimeException(“unable to handshake with server”);

56: cb.addPacketListener(this);

57: initCache();

58: startReports();

59: }

60: // Ask XDB for the initial subscription data.

61: private void initCache() throws InstantiationException {

62: XDBBuilder xdb = new XDBBuilder();

63: xdb.setType(“get”);

64: xdb.setToAddress(myJid);

65: xdb.setFromAddress(myJid);

Listing 5.8 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 181

Page 199: Jabber Developer’s Handbook [Sams 2004]

182 Extending the Jabber Server

66: xdb.setNamespace(ReportData.XMLNS);

67: XDB xdbPacket = (XDB) xdb.build();

68: cb.send(xdbPacket);

69: }

70: // PacketListener interface. Called when any packet is received.

71: // Delegates based on the type of packet (IQ, XDB, or Message)

72: public void receivedPacket(PacketEvent packetEvent) {

73: Packet packet = packetEvent.getPacket();

74: try {

75: if (packet instanceof InfoQuery)

76: handleIQ((InfoQuery) packet);

77: else if (packet instanceof Message)

78: handleMessage((Message) packet);

79: else if (packet instanceof XDB)

80: handleXDB((XDB) packet);

81: } catch (Exception e) {

82: e.printStackTrace();

83: }

84: }

85: // PacketListener interface. Called when any packet is sent.

86: public void sentPacket(PacketEvent packetEvent) {}

87: // PacketListener interface. Called when a packet send fails.

88: public void sendFailed(PacketEvent packetEvent) {}

89: // Respond to XDB packets by updating the cache if necessary.

90: private void handleXDB(XDB xdb) throws InstantiationException {

91: Enumeration items = xdb.Extensions();

92: if (items.hasMoreElements()) {

93: ReportData rd = (ReportData) items.nextElement();

94: Enumeration jids = rd.getUsers();

95: while (jids.hasMoreElements()) {

96: JID jid = (JID) jids.nextElement();

97: cache.put(jid, rd.getReportList(jid));

98: }

99: }

100: }

101: // Respond to Message packets by generating the reports for the

102: // user sending us the message.

103: private void handleMessage(Message mess) throws InstantiationException {

104: MessageBuilder mbuilder = new MessageBuilder();

105: mbuilder.setToAddress(mess.getFromAddress());

106: mbuilder.setFromAddress(mess.getToAddress());

Listing 5.8 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 182

Page 200: Jabber Developer’s Handbook [Sams 2004]

183A Report Service

107: mbuilder.setThread(mess.getThread());

108: mbuilder.setType(mess.getType());

109: JID sender =

110: new JID(

111: mess.getFromAddress().getUsername(),

112: mess.getFromAddress().getServer(),

113: null);

114: mbuilder.setBody(“Generating reports: “ + cache.get(sender));

115: cb.send(mbuilder.build());

116: generateReports(sender, (String) cache.get(sender));

117: }

118: // Respond to IQ packets. Delegates based on the type of message

119: // (register or search result)

120: private void handleIQ(InfoQuery iq) {

121: try {

122: Enumeration enum = iq.Extensions();

123: while (enum.hasMoreElements()) {

124: Extension ex = (Extension) enum.nextElement();

125: if (ex instanceof IQRegister) {

126: if (iq.getType().equals(“get”)) {

127: giveInstructions(iq, (IQRegister) ex);

128: } else {

129: handleRegister(iq, (IQRegister) ex);

130: }

131: } else if (ex instanceof IQSearchResult) {

132: handleSearchResult(iq, (IQSearchResult) ex);

133: }

134: }

135: } catch (Exception ex) {

136: ex.printStackTrace();

137: }

138: }

139: // Turn search results into reports.

140: private void handleSearchResult(InfoQuery iq, IQSearchResult sr)

141: throws InstantiationException {

142: JID requestor = (JID) outstandingQueries.remove(iq.getIdentifier());

143: MessageBuilder mb = new MessageBuilder();

144: mb.setToAddress(requestor);

145: mb.setFromAddress(myJid);

146: mb.setSubject(“Your Report: “ + iq.getFromAddress().getResource());

147: mb.setBody(formatReport(sr));

148: cb.send(mb.build());

149: }

Listing 5.8 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 183

Page 201: Jabber Developer’s Handbook [Sams 2004]

184 Extending the Jabber Server

150: // Register users to get reports (register IQs of type “set”)

151: private void handleRegister(InfoQuery iq, IQRegister iqr)

152: throws InstantiationException {

153: JID sender =

154: new JID(

155: iq.getFromAddress().getUsername(),

156: iq.getFromAddress().getServer(),

157: null);

158: XDBBuilder xdb = new XDBBuilder();

159: xdb.setType(“set”);

160: xdb.setAction(“insert”);

161: xdb.setToAddress(iq.getToAddress());

162: xdb.setFromAddress(iq.getToAddress());

163: xdb.setNamespace(ReportData.XMLNS);

164: xdb.setMatch(“?jid=” + sender.toSimpleString());

165: ReportDataBuilder rdb = new ReportDataBuilder();

166: String reportList = iqr.getValue(“reports”);

167: if (reportList == null)

168: reportList = “”;

169: rdb.setReportList(sender, reportList);

170: cache.put(sender, reportList);

171: xdb.addExtension(rdb.build());

172: cb.send(xdb.build());

173: // report success to the sender

174: InfoQueryBuilder iqb = new InfoQueryBuilder();

175: iqb.setFromAddress(iq.getToAddress());

176: iqb.setToAddress(iq.getFromAddress());

177: iqb.setType(“result”);

178: iqb.setIdentifier(iq.getIdentifier());

179: cb.send(iqb.build());

180: }

181: // Provide registration instructions (register IQs of type “get”)

182: private void giveInstructions(InfoQuery iq, IQRegister iqr)

183: throws InstantiationException {

184: IQRegisterBuilder iqrb = new IQRegisterBuilder();

185: iqrb.set(

186: “instructions”,

187: “Enter the names of the reports you desire separated by commas.”);

188: iqrb.set(“reports”, “”);

189: InfoQueryBuilder iqb = new InfoQueryBuilder();

190: iqb.addExtension(iqrb.build());

191: iqb.setFromAddress(iq.getToAddress());

192: iqb.setToAddress(iq.getFromAddress());

193: iqb.setType(“result”);

Listing 5.8 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 184

Page 202: Jabber Developer’s Handbook [Sams 2004]

185A Report Service

194: iqb.setIdentifier(iq.getIdentifier());

195: cb.send(iqb.build());

196: }

197: // A Hastable to keep track of requests that we sent to the DB service.

198: private Hashtable outstandingQueries = new Hashtable();

199: // Generate and send the reports

200: private void generateReports(JID user, String reports)

201: throws InstantiationException {

202: StringTokenizer toker = new StringTokenizer(reports, “,”);

203: while (toker.hasMoreTokens()) {

204: String report = toker.nextToken().trim();

205: InfoQueryBuilder iqb = new InfoQueryBuilder();

206: iqb.setFromAddress(myJid);

207: iqb.setToAddress(JID.fromString(dbService + report));

208: iqb.addExtension(new IQSearchRequestBuilder().build());

209: iqb.setType(“set”);

210: InfoQuery iq = (InfoQuery) iqb.build();

211: outstandingQueries.put(iq.getIdentifier(), user);

212: cb.send(iq);

213: }

214: }

215: // Make the reports look nice (well, kind of nice...)

216: private static String formatReport(IQSearchResult sr) {

217: Enumeration items = sr.getResults();

218: StringBuffer retval = new StringBuffer(1024);

219: Vector names = null;

220: while (items.hasMoreElements()) {

221: SearchResult item = (SearchResult) items.nextElement();

222: if (names == null) {

223: names = new Vector();

224: Enumeration names_enum = item.getNames();

225: while (names_enum.hasMoreElements()) {

226: String name = (String) names_enum.nextElement();

227: names.add(name);

228: retval.append(name);

229: if (names_enum.hasMoreElements())

230: retval.append(“,”);

231: }

232: retval.append(“\n”);

233: }

234: Enumeration names_enum = names.elements();

235: while (names_enum.hasMoreElements()) {

236: retval.append(item.getValue((String) names_enum.nextElement()));

Listing 5.8 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 185

Page 203: Jabber Developer’s Handbook [Sams 2004]

186 Extending the Jabber Server

237: if (names_enum.hasMoreElements())

238: retval.append(“,”);

239: }

240: retval.append(“\n”);

241: }

242: return retval.toString();

243: }

244: // Timer task that generates all reports for all subscribers

245: private class ReportTask extends TimerTask {

246: public void run() {

247: Hashtable reports = (Hashtable) cache.clone();

248: Enumeration users = reports.keys();

249: while (users.hasMoreElements()) {

250: JID user = (JID) users.nextElement();

251: try {

252: generateReports(user, (String) reports.get(user));

253: } catch (InstantiationException ie) {

254: ie.printStackTrace();

255: }

256: }

257: }

258: }

259: // Start up the report generation timer.

260: private void startReports() {

261: final long MILLIS_PER_DAY = 86400000;

262: Timer timer = new Timer();

263: timer.scheduleAtFixedRate(new ReportTask(), 10000, MILLIS_PER_DAY);

264: }

265: // This is where it all starts

266: public static void main(String[] args) {

267: new ReportService(args);

268: }

269:}

RegistrationThe jabber.xml configuration of this service is also similar to the database service, butthe namespace used in the JSM <browse> configuration shows that this service canaccept registrations:

<service type=”report” jid=”reports.my-jabber” name=”Report Service”>

<ns>jabber:iq:register</ns>

</service>

Listing 5.8 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 186

Page 204: Jabber Developer’s Handbook [Sams 2004]

187A Report Service

The inclusion of the jabber:iq:register namespace tells the IM client that it canpresent a registration option as shown in Figure 5.9.

Figure 5.9 Registering with the reports service.

When the Register option is selected, the IM client sends a packet to the servicerequesting registration instructions.This is much like the search scenario you saw in theprevious example:A get packet tells the service to respond with instructions for registra-tion, and a set packet contains the actual registration.The packet to request registrationinstructions looks like this:

<iq to=”reports.my-jabber” from=”user1@my-jabber/Exodus”

id=”jcl_16” type=”get”>

<query xmlns=”jabber:iq:register”></query>

</iq>

It’s identical to the search get request you saw earlier, except for the XML namespaceof the <query> element.This packet gets routed to the reports service and ends up at thegiveInstructions() method (line 182). Here we insert the instructions (line 185) intothe IQRegister JabberBeans object. Just one entry field is required to register with thisservice—a list of the reports to send—so there’s just one element in this response:<reports> (line 188).The rest of the giveInstructions() method addresses and sendsthe response, which looks like this:

<iq to=”user1@my-jabber/Exodus” from=”reports.my-jabber”

id=”jcl_16” type=”result”>

<query xmlns=”jabber:iq:register”>

<reports/>

<instructions>

Enter the names of the reports you desire separated by commas.

</instructions>

</query>

</iq>

07 0672325365 CH05 1/21/05 2:24 PM Page 187

Page 205: Jabber Developer’s Handbook [Sams 2004]

188 Extending the Jabber Server

NOTEWe should mention that, at the time of this writing, the Jabber protocol technically does not allow a

<reports> tag within a jabber:iq:register packet. None of the libraries or clients that we’ve

used have a problem with the extra tag, but some might. One solution to this problem would be to use one

of the approved tags (<misc>, for example) rather than <reports>, but this makes the code and the

XML packets less clear, so we’ll stick with <reports>.

As in the search case in the previous example, the <instructions> element is not aregistration field to be filled in, but contains text to be displayed to the user.The ExodusIM client generates a three-stage wizard to handle registration.The first window justcontains the instructions text from the jabber:iq:register result packet.The secondwindow is shown in Figure 5.10.

Figure 5.10 A registration wizard generated by the IM client.

The client has built a form with the field that the service told it about (“reports”). If wefill in the form as shown in Figure 5.10 and press Next, the client generates this setpacket to register with the service:

<iq to=”reports.my-jabber” from=”user1@my-jabber/Exodus”

id=”JCOM_4” type=”set”>

<query xmlns=”jabber:iq:register”>

<reports>sales,personnel</reports>

</query>

</iq>

This packet gets routed to the reports service, where it ends up at thehandleRegistration() method (line 151).

A Small Digression: A Custom Packet Type for JabberBeansThe data elements to be stored as records of the clients’ registrations are not a type ofdata that the JabberBeans library knows how to process. XDB doesn’t care—it just takes

07 0672325365 CH05 1/21/05 2:24 PM Page 188

Page 206: Jabber Developer’s Handbook [Sams 2004]

189A Report Service

XML and stores it in a file—but JabberBeans parses XML into Java objects to makethem easier to handle in a Java program. It also takes those objects and converts them toXML when packets are sent.The examples so far use several of those objects for browse,search, and register operations and these objects are created automatically when a packetis received and passed to the receivedPacket() method of the PacketListener inter-face. JabberBeans uses the XML namespace of the incoming packet to determine whichtype of object to create to represent that packet.This section describes how to create theclasses necessary to handle the custom namespace necessary for the report service. If thisdetail isn’t of interest to you, feel free to skip to the next section.

Adding a custom packet handler requires three classes:n The data class. Represents the packet or extension itself.This class does the

XML encoding for outgoing packets and is the class that the report service uses toread incoming packets.

n A builder class. Has the setter methods used to construct a new instance of thepacket or extension class.This is the class that the report service uses to create newinstances of the custom extension.

n An XML parser class. Parses the XML stream and uses the builder class to createan instance of the packet or extension data class.

It’s useful to visualize the data on which these classes are operating. Here’s an example ofthe ReportData record that we need for the report service:

<report xmlns=”jdh:data:report” jid=”user1@my-jabber”>

sales,personnel

</report>

<report xmlns=”jdh:data:report” jid=”user2@my-jabber”>

personnel,facilities

</report>

Pretty simple. It’s a single element extension, repeated some number of times.Theelement tag is report, the XML namespace is jdh:data:report, and there is a JIDattribute.The content of the element is just a simple string.The data and builder class-es will need accessors for the XML namespace, the JID, and the contents.The XMLnamespace is always the same, but JabberBeans needs a read accessor so it can determinewhich parser to use for incoming packets.

Listing 5.9 shows the data class.This class implements QueryExtension, which meansthat it is a part of a larger packet and it extends the abstract class XMLData, which gives itaccessors for the XML namespace, among other things.The only variable data that mustbe stored is the list of JID-to-report lists.This is easy to store in the Java Hashtablecalled map (line 13), so the Hashtable’s methods are exposed to get the list of JIDs (lines20–22) and the reports associated with a particular JID (lines 24–26).

07 0672325365 CH05 1/21/05 2:24 PM Page 189

Page 207: Jabber Developer’s Handbook [Sams 2004]

190 Extending the Jabber Server

Listing 5.9 JabberBeans Data Class for Report Data

1:package jdh.demos;

2:import java.util.*;

3:import org.jabber.jabberbeans.XMLData;

4:import org.jabber.jabberbeans.Extension.QueryExtension;

5:import org.jabber.jabberbeans.util.JID;

6:

7:/**

8: * A simple holder for report subscription data.

9: * Not much more than a Hashtable mapping JID->subscription list.

10: */

11:public class ReportData extends XMLData implements QueryExtension {

12: final static public String XMLNS = “jdh:data:report”;

13: private Hashtable map;

14:

15: public ReportData(ReportDataBuilder builder) {

16: map = builder.getMap();

17: setXMLNS(XMLNS);

18: }

19:

20: public Enumeration getUsers() {

21: return map.keys();

22: }

23:

24: public String getReportList(JID user) {

25: return (String) map.get(user);

26: }

27:

28: // This is the method that converts the object to XML

29: public void appendItem(StringBuffer retval) {

30: Enumeration jids = getUsers();

31: while (jids.hasMoreElements()) {

32: JID jid = (JID) jids.nextElement();

33: retval.append(“<report xmlns=\””);

34: retval.append(getXMLNS());

35: retval.append(“\” jid=\””);

36: retval.append(jid.toSimpleString());

37: retval.append(“\”>”);

38: retval.append(getReportList(jid));

39: retval.append(“</report>”);

40: }

41: }

42:}

07 0672325365 CH05 1/21/05 2:24 PM Page 190

Page 208: Jabber Developer’s Handbook [Sams 2004]

191A Report Service

The constructor (lines 15–18) uses the builder to initialize itself.This is how a newReportData object gets created.The appendItem() abstract method from the XMLDataclass actually encodes the contents of the object as XML into the StringBuffer.ThisStringBuffer is the one used by JabberBeans to construct an outgoing packet.

The builder class contains the methods that the report service uses to set the values ofthe data object for an outgoing packet.The parser class also uses the builder class to setthe values of a data object for incoming packets.

Listing 5.10 JabberBeans Builder Class for Report Data

1:package jdh.demos;

2:import java.util.Hashtable;

3:import org.jabber.jabberbeans.Extension.*;

4:import org.jabber.jabberbeans.util.JID;

5:

6:/**

7: * A jabberbeans builder class for constructing ReportData objects.

8: */

9:public class ReportDataBuilder implements ExtensionBuilder {

10: private Hashtable map = new Hashtable();

11: public ReportDataBuilder() {

12: reset();

13: }

14:

15: public ReportDataBuilder(Hashtable init) {

16: map = (Hashtable) init.clone();

17: }

18: public void reset() {

19: map.clear();

20: }

21:

22: public String getReportList(JID user) {

23: return (String) map.get(user);

24: }

25:

26: public void setReportList(JID user, String reports) {

27: map.put(user, reports);

28: }

29:

30: public Extension build() {

31: return new ReportData(this);

32: }

33: protected Hashtable getMap() {

34: return map;

35: }

36:

37:}

07 0672325365 CH05 1/21/05 2:24 PM Page 191

Page 209: Jabber Developer’s Handbook [Sams 2004]

192 Extending the Jabber Server

The builder class is similar to the data class, but it includes methods to set the data aswell as read it.The build() (lines 30–32) method of the ExtensionBuilder interface isused to construct an instance of the data class after the builder has been configured byeither the report service or the parser.

The parser (called a handler by convention) uses the builder to convert the incomingXML stream into the data object. JabberBeans handlers are SAX parsers, so they containcallback methods that are invoked when a new element starts, characters within the ele-ment are encountered, and so on.

Listing 5.11 JabberBeans Handler (Parser) Class

1:package jdh.demos;

2:import org.jabber.jabberbeans.sax.SubHandler;

3:import org.jabber.jabberbeans.util.JID;

4:import org.xml.sax.AttributeList;

5:import org.xml.sax.SAXException;

6:

7:/**

8: * Handler class to build report subscription XML documents.

9: */

10:public class ReportDataHandler extends SubHandler {

11: StringBuffer elementChars;

12: JID jid;

13: ReportDataBuilder builder;

14: private static final String elementName = “report”;

15:

16: public ReportDataHandler() {

17: builder = new ReportDataBuilder();

18: }

19:

20: protected void startHandler(String name, AttributeList attributes)

21: throws SAXException {

22: elementChars = new StringBuffer();

23: builder.reset();

24: }

25:

26: public void characters(char[] ch, int start, int length)

27: throws SAXException {

28: elementChars.append(ch, start, length);

29: }

30:

31: protected void handleStartElement(String name, AttributeList attributes)

32: throws SAXException {

33: if (elementName.equals(name)) {

34: jid = JID.fromString(attributes.getValue(“jid”));

35: }

07 0672325365 CH05 1/21/05 2:24 PM Page 192

Page 210: Jabber Developer’s Handbook [Sams 2004]

193A Report Service

36: elementChars = new StringBuffer();

37: }

38:

39: protected void handleEndElement(String name) throws SAXException {

40: if (elementName.equals(name))

41: builder.setReportList(jid, new String(elementChars));

42: }

43:

44: protected Object stopHandler(String name) {

45: if (elementName.equals(name))

46: builder.setReportList(jid, new String(elementChars));

47: return builder.build();

48: }

49:}

The JID and elementChars methods are used to keep track of the current elementbeing parsed.After the whole list of <report> elements has been scanned, thestopHandler() method is called, which returns the completed ReportData object.

So how does the JabberBeans library know about this parser class and that it shouldbe invoked for the XML namespace jdh:data:report? A configuration file calledProperties/Handlers.properties, included in the jabberbeans.jar file, contains thisinformation.As delivered, it includes entries for the other packet and extension classesseen in the previous examples. Listing 5.12 shows the section of Handlers.propertiesthat we need to add to configure the new ReportData handler.

Listing 5.12 Configuring the JabberBeans ReportData Handler

1: xmlns.report.desc = Data storage for report service

2: xmlns.report.ns = jdh:data:report

3: xmlns.report.builder = jdh.demos.ReportDataBuilder

4: xmlns.report.handler = jdh.demos.ReportDataHandler

5: xmlns.report.product = jdh.demos.ReportData

This section, added to the end of Handlers.properties, provides an arbitrary descriptionof the data type (line 1), the namespace that identifies this extension (line 2), and thethree classes used to build it (lines 3–5).After these four files are in place, JabberBeanscalls the custom handler when it sees the custom namespace.

Custom Storage in XDBListing 5.13 is the method that handles client registration (repeated from Listing 5.8 forconvenience).This method has to do two things: store the client’s registration data andtell the client that the registration was successful.The latter is easy; just send the client an

Listing 5.11 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 193

Page 211: Jabber Developer’s Handbook [Sams 2004]

194 Extending the Jabber Server

IQ packet of type result with the same ID as the packet that the client sent. If there isan error, an error packet like you already saw in the database service example should begenerated.

Listing 5.13 Client Registration

151: private void handleRegister(InfoQuery iq, IQRegister iqr)

152: throws InstantiationException {

153: JID sender =

154: new JID(

155: iq.getFromAddress().getUsername(),

156: iq.getFromAddress().getServer(),

157: null);

158: XDBBuilder xdb = new XDBBuilder();

159: xdb.setType(“set”);

160: xdb.setAction(“insert”);

161: xdb.setToAddress(iq.getToAddress());

162: xdb.setFromAddress(iq.getToAddress());

163: xdb.setNamespace(ReportData.XMLNS);

164: xdb.setMatch(“?jid=” + sender.toSimpleString());

165: ReportDataBuilder rdb = new ReportDataBuilder();

166: String reportList = iqr.getValue(“reports”);

167: if (reportList == null)

168: reportList = “”;

169: rdb.setReportList(sender, reportList);

170: cache.put(sender, reportList);

171: xdb.addExtension(rdb.build());

172: cb.send(xdb.build());

173: // report success to the sender

174: InfoQueryBuilder iqb = new InfoQueryBuilder();

175: iqb.setFromAddress(iq.getToAddress());

176: iqb.setToAddress(iq.getFromAddress());

177: iqb.setType(“result”);

178: iqb.setIdentifier(iq.getIdentifier());

179: cb.send(iqb.build());

180: }

The interesting part of this method is the storage of the data in XDB.The key to storeand retrieve the data will be the JID of the sender (not including the resource), which isgenerated at lines 153–157.The JabberBeans XDBBuilder in line 158 is used to constructthe packet that will be sent to XDB.An XDB packet is similar to IQ packets, but hassome additional attributes.The action attribute (line 160) determines what type ofoperation this is (an insertion, in this case).

07 0672325365 CH05 1/21/05 2:24 PM Page 194

Page 212: Jabber Developer’s Handbook [Sams 2004]

195A Report Service

TIPYou might assume (as we did) that other recognized values of the action attribute would be things such

as update to change the value of existing data and delete to remove data. Actually, the only other rec-

ognized value is check, which compares the data in the packet with the stored data. The only way to

delete or update data is to use insert to overwrite it. Go figure.

The XML namespace (line 163) also serves as a storage key. Here we’re using a newnamespace that we’ll discuss more later. Lines 161 and 162 look like a typo; they set theto and from addresses to both be the address of the report service.All XDB packets goto XDB, so the to address of an XDB packet tells XDB how to store the data. If the toaddress is the address of a user (for example, user@server), XDB stores the data with theuser’s other data, such as his vCard and roster information. If the address is just a servicename (for example, service.server), XDB stores the data in a file just for that servicecalled global.xdb, where it can be retrieved for use by that service.This is the type ofstorage the report service uses. Finally, the match attribute (line 164) provides the keythat XDB uses to store and search this data.The XDB packet generated by the registra-tion example looks like this:

<xdb to=”reports.my-jabber” from=”reports.my-jabber” id=”sn-6” type=”set”

ns=”jdh:data:report” action=”insert” match=”?jid=user1@my-jabber”>

<report xmlns=”jdh:data:report” jid=”user1@my-jabber”>

sales,personnel

</report>

</xdb>

XDB responds with this success packet:

<xdb to=”reports.my-jabber” from=”reports.my-jabber” id=”sn-6” type=”result”

ns=”jdh:data:report” action=”insert” match=”?jid=user1@my-jabber”>

<report xmlns=”jdh:data:report” jid=”user1@my-jabber”>

sales,personnel

</report>

</xdb>

This looks familiar. It’s exactly the same as the packet we sent to XDB with the typechanged to result.This packet gets routed to the handleXDB() method (lines 90–100),where we go through the ReportData objects and pull out each JID and its associatedlist of reports, and store them in a Hashtable cache, so they’re always close at hand. Byupdating the cache here rather than when we send the data to XDB, we can use thismethod for two purposes.At the end of the initialization when the service is first started,it sends an XDB request in the initCache() method (lines 61–69). In this request, wedon’t specify a match attribute, so XDB sends the entire data file.This response also endsup at handleXDB(), so the cache is initialized and updated in the same manner.

So where did XDB save this data? It created a new directory in the spool directory(specified in jabber.xml) for this service’s data called reports.my-jabber and wrote a

07 0672325365 CH05 1/21/05 2:24 PM Page 195

Page 213: Jabber Developer’s Handbook [Sams 2004]

196 Extending the Jabber Server

single file in it called global.xdb.This file contains just an XML document fragmentthat holds the data as delivered to XDB. Here’s an example global.xdb file:

<xdb>

<foo xdbns=’jdh:data:report’ xmlns=’jdh:data:report’>

<report xmlns=’jdh:data:report’ jid=’user2@my-jabber’>

personnel,facilities

</report>

<report xmlns=’jdh:data:report’ jid=’user1@my-jabber’>

sales,personnel

</report>

</foo>

</xdb>

This file contains two ReportData records, which XDB has wrapped in a <foo> ele-ment to sort it by XML namespace.

Timed Report GenerationThat’s a lot of housekeeping; let’s generate some reports.This service generates reportsunder two conditions:

n A timer expiration causes reports to be generated every day for all users.n If it receives a message from a user, the service generates reports for that user.

As we mentioned, the capability to generate reports when the service receives a mes-sage is a useful debugging device, but the actions are the same as those taken when atimer expires, so let’s look at what happens when the timer fires.

The Java timer (configured at line 263) calls the ReportTask method, starting at line246, which loops through all the users in the cache and generates their reports by callinggenerateReports() (repeated in Listing 5.14 for convenience).

Listing 5.14 Calling the Database Service

197: // A Hastable to keep track of requests that we sent to the DB service.

198: private Hashtable outstandingQueries = new Hashtable();

199: // Generate and send the reports

200: private void generateReports(JID user, String reports)

201: throws InstantiationException {

202: StringTokenizer toker = new StringTokenizer(reports, “,”);

203: while (toker.hasMoreTokens()) {

204: String report = toker.nextToken().trim();

205: InfoQueryBuilder iqb = new InfoQueryBuilder();

206: iqb.setFromAddress(myJid);

207: iqb.setToAddress(JID.fromString(dbService + report));

208: iqb.addExtension(new IQSearchRequestBuilder().build());

209: iqb.setType(“set”);

210: InfoQuery iq = (InfoQuery) iqb.build();

07 0672325365 CH05 1/21/05 2:24 PM Page 196

Page 214: Jabber Developer’s Handbook [Sams 2004]

197A Report Service

211: outstandingQueries.put(iq.getIdentifier(), user);

212: cb.send(iq);

213: }

214: }

This method loops through each report and builds an IQ packet addressed to the data-base service. Line 206 builds the JID for the database service, including the table name(called “report” here).We want to fetch the whole table, so we add an empty searchrequest (line 208) and send the packet on its way (line 212).We’ll need to match thereply from the database service with this request, so we keep a Hashtable mapping theID of this message to the client to which we’ll need to send the report (line 211).Thesepackets look just like the ones sent by the IM client in the previous example—the dif-ference is that their “from” address is another service rather than a client.

The Jabber server doesn’t care that this is another service and not a client—the IQresponse packets get routed back to the report service and end up at thehandleSearchResult() method (Listing 5.15).

Listing 5.15 Formatting the Database Results for the Client

140: private void handleSearchResult(InfoQuery iq, IQSearchResult sr)

141: throws InstantiationException {

142: JID requestor = (JID) outstandingQueries.remove(iq.getIdentifier());

143: MessageBuilder mb = new MessageBuilder();

144: mb.setToAddress(requestor);

145: mb.setFromAddress(myJid);

146: mb.setSubject(“Your Report: “ + iq.getFromAddress().getResource());

147: mb.setBody(formatReport(sr));

148: cb.send(mb.build());

149: }

This method creates a new Message packet (finally, a regular message!) and addresses it tothe client (line 144). It sets the message’s subject by using the name of the report, whichis in the resource field of the IQ packet (line 146). It calls the formatReport() helpermethod to convert the XML search result into something readable in a message, andthen sends the message (line 148).

NOTESome IM clients can display HTML formatted messages. An interesting enhancement would be to make the

formatReport() method output an HTML table. Some clients can understand message attachments.

Perhaps the report could be delivered as an attached spreadsheet.

Once a day, the reports are sent to the registered clients. If the client is an instant mes-saging client, the message will be displayed as in Figure 5.11.

Listing 5.14 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 197

Page 215: Jabber Developer’s Handbook [Sams 2004]

198 Extending the Jabber Server

Figure 5.11 A report as shown by an IM client.

This user has received two reports, as shown in the message list in the upper right pane.One of the reports is visible in the lower left pane.

An Inventory Management ServiceFinally, here’s a simple example that shows interacting with a component style serviceand leveraging the native capability of a language to serialize data rather than using theXDB services discussed earlier.We also demonstrate presences as an attribute of a serviceoffering.

In this example, multiple users can add a component service to their roster, and chatwith the service as though it were another client. For illustrative purposes, we imple-mented a simple semantic consisting of the three simple directives shown later in Figure5.14 (which shows the Registration Wizard in operation).

Through the service, users can create an inventory, add items into the inventory,decrement the inventory, and list the inventory’s contents. If the service goes offline, itsaves the inventory using Python’s pickle serialization.When the service comes backonline, it will restore the inventory, restore its knowledge of its user base, then reconnectwith its user base.The users, having added the service to their roster, will see the service’savailability as it comes online and occasionally goes offline (for example, to repair a bugor enhance capabilities).

What’s also interesting and illustrative in this example is that it shows the strategyused for registering a user to a service.

Let’s look at the user experience first and then understand the code.An ordinary user scans the available services and chooses the Inventory Manager, as

shown in Figure 5.12.

07 0672325365 CH05 1/21/05 2:24 PM Page 198

Page 216: Jabber Developer’s Handbook [Sams 2004]

199An Inventory Management Service

Figure 5.12 Available services.

In this example, we choose Register with a Service from the client’s menu and type inthe name of the service (stockist), as shown in Figure 5.13.

Figure 5.13 Registering with the service.

The service responds with a dialogue, explaining itself.The explanation is a part of theservice description package that the service sends a client in response to a jabber:iq:register message.

Figure 5.14 The service “wizard.”

07 0672325365 CH05 1/21/05 2:24 PM Page 199

Page 217: Jabber Developer’s Handbook [Sams 2004]

200 Extending the Jabber Server

After the service and the client have completed negotiations, the service appears as anormal roster item, and can respond to conversations, using the vocabulary shown inFigure 5.14.

Figure 5.15 Service appears as a roster item.

In Figure 5.16, a client performs several operations, including listing the inventory,adding apples to an existing inventory, taking lemons from the inventory, and creatingand stocking a new inventory category: raspberry.

Figure 5.16 Chatting with the service.

Because this is a simple example meant as a teaching device, the service is not transac-tion safe and has no authentication or access protection. In short, it’s meant to be a funteaching example.

Let’s see how it works.The code for the overall control of the service is shown intoto in Listing 5.16, and the client’s proxy is shown in Listing 5.17.

07 0672325365 CH05 1/21/05 2:24 PM Page 200

Page 218: Jabber Developer’s Handbook [Sams 2004]

201An Inventory Management Service

Listing 5.16 InvComponent.py

1:import jabber

2:import sys

3:import sha

4:import pickle

5:import string

6:import sys, re, os, string

7:from inventoryClient import InventoryClient

8:keys = {}

9:clients = {}

10:myFromID = ‘stockist’

11:re_item = re.compile(“\W(.*)\W”)

12:# Global procedures:

13:def iqCB(con, iq):

14: resultIq = jabber.Iq(to=iq.getFrom())

15: resultIq.setID(iq.getID())

16: resultIq.setFrom(iq.getTo())

17: myFromID = iq.getTo()

18: query_result = resultIq.setQuery(iq.getQuery())

19: type = iq.getType()

20: query_ns = iq.getQuery()

21: if query_ns == jabber.NS_REGISTER:

22: if type == ‘get’:

23: print ‘ jabber.NS_REGISTER. type== get’

24: resultIq.setType(‘result’)

25: iq_from = str(iq.getFrom())

26: keys[iq_from] = sha.new(iq_from)

27: # explain the client the fields we want

28: fields = {

29: ‘instructions’:

30: “””

31:Welcome to the inventory manager. Command set:

32:(l)ist :: show inventory

33:(p)ut (item-descr) quantity :: add inventory.

➥ please note required use of ‘()’

34:(t)ake (item-descr) quantity :: decr inventory.

➥ please note required use of ‘()’

35: “””,

36: ‘key’: keys[iq_from].hexdigest()

37: }

38: for field in fields.keys():

39: field_node = query_result.insertTag(field)

07 0672325365 CH05 1/21/05 2:24 PM Page 201

Page 219: Jabber Developer’s Handbook [Sams 2004]

202 Extending the Jabber Server

40: if fields[field]:

41: field_node.putData(fields[field])

42: print ‘sending resultIq. type = ‘+(resultIq.getType())

43: con.send(resultIq)

44: elif type == ‘set’:

45: client_key_node = iq.getQueryNode().getTag(‘key’)

46: if not client_key_node:

47: resultIq.setType(‘error’)

48: resultIq.setError(‘no key given!’)

49: con.send(resultIq)

50: else:

51: if keys[str(iq.getFrom())].hexdigest() ==

➥ client_key_node.getData():

52: if iq.getQueryNode().getTag(‘remove’):

53: del clients[iq.getFrom().getStripped()]

54: else:

55: jid = iq.getFrom().getStripped()

56: client = InventoryClient(jid, con, myFromID)

57: clients[client.jid] = client

58: # subscribe to the client’s presence

59: sub_req = jabber.Presence(iq.getFrom(),

➥ type=’subscribe’)

60: sub_req.setFrom(str(iq.getTo()) + “/registered”)

61: con.send(sub_req)

62: resultIq.setType(‘result’)

63: con.send(resultIq)

64: else:

65: resultIq.setType(‘error’)

66: resultIq.setError(‘invalid key’, 400)

67: con.send(resultIq)

68: # done with key; delete it

69: del keys[str(iq.getFrom())]

70: else:

71: print “don’t know how to handle type”, type, “for query”,

➥ query_ns

72:

73: elif (query_ns == jabber.NS_AGENT) and (type == ‘get’):

74: resultIq.setType(‘result’)

Listing 5.16 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 202

Page 220: Jabber Developer’s Handbook [Sams 2004]

203An Inventory Management Service

75: responses = {

76: ‘name’: “Inventory Manager Component”,

77: # ‘url’: None,

78: ‘description’: “this is the Inventory Manager Component”,

79: ‘transport’: “<future>...”,

80: ‘register’: None, # we can be registered with

81: ‘service’: ‘test’ # nothing really standardized here...

82: }

83: for response in responses.keys():

84: resp_node = query_result.insertTag(response)

85: if responses[response]:

86: resp_node.putData(responses[response])

87: con.send(resultIq)

88: else:

89: print “don’t know how to handle type”, type, “for query”, query_ns

90:def presenceCB(con, pres):

91: p = jabber.Presence(to=pres.getFrom())

92: p.setFrom(pres.getTo())

93:

94: type = pres.getType()

95: # find the client object

96: if str(pres.getFrom().getStripped()) in clients.keys():

97: client = clients[pres.getFrom().getStripped()]

98: else:

99: print(“not able to find client for “ + pres.getFrom().getStripped())

100:

101: client = None

102: if type != ‘unsubscribed’:

103: type = ‘unsubscribe’

104:

105: if not type:

106: type = ‘available’

107: print(pres.getFrom().getStripped() + “ is “ + type)

108: if type == ‘unavailable’:

109: # user went offline

110: client.setOnline(0)

111: p.setType(‘unavailable’)

112: con.send(p)

113:

Listing 5.16 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 203

Page 221: Jabber Developer’s Handbook [Sams 2004]

204 Extending the Jabber Server

114: elif type == ‘subscribe’:

115: # user wants to subscribe to our presence; oblige, and ask for his

116: p.setType(‘subscribed’)

117: con.send(p)

118: p.setType(‘subscribe’)

119: con.send(p)

120: elif type == ‘unsubscribe’:

121: p.setType(‘unsubscribed’)

122: con.send(p)

123: p.setType(‘unsubscribe’)

124: con.send(p)

125:

126: elif type == ‘unsubscribed’:

127: # now unsubscribe from the user’s presence

128: pass

129: elif type == ‘probe’:

130: # send our presence

131: p.setType(‘available’)

132: con.send(p)

133:

134: elif type == ‘available’:

135: # user is online

136: client.setStatus(pres.getStatus())

137: client.setShow(pres.getShow())

138: p.setType(‘available’)

139: con.send(p)

140:def messageCB(con, msg):

141: m = jabber.Message()

142: m.setFrom(msg.getTo())

143: m.setTo(msg.getFrom())

144: print “messageCB: “+msg.getBody()

145: if msg.getType() == ‘chat’: m.setType(msg.getType())

146: m.setBody(produceContent(msg.getBody()))

147: # parse

148: type = msg.getType()

149: if type == None: type = ‘normal’

150: # deal with interaction type

151: if type == ‘chat’ or type == ‘normal’:

152: jid = str(msg.getFrom())

153: message = “Message from “ + jid + “:” + msg.getBody()

154: print message

155: con.send(m)

Listing 5.16 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 204

Page 222: Jabber Developer’s Handbook [Sams 2004]

205An Inventory Management Service

156:def produceContent(line):

157: atoms = re.split( re_item, line)

158: operation = string.lower(atoms[0])

159: if len(operation) == 0:

160: return ‘Ready for input:\n’+getInventory()

161: if (operation[0] == ‘l’):

162: return getInventory()

163: if (operation[0] <> ‘p’ and operation[0] <> ‘t’):

164: return “Sorry, Command not understood”

165: item = atoms[1]

166: item = item[1:-1]

167: quantity = int(atoms[2])

168: return adjust(operation, item, quantity)

169:

170:def adjust(operation, item, quant):

171: if item not in inventory:

172: # setup new item

173: inventory[item] = quant

174: return “Created inventory for “+item+”. Stocked at “+

➥ str(inventory[item] )

175: if operation[0] == ‘p’:

176: curr = inventory[item]

177: inventory[item] = curr + quant

178: return “Added inventory for “+item+”. Stocked at “+

➥ str(inventory[item])

179: if operation[0] == ‘t’:

180: curr = inventory[item]

181: inventory[item] = curr - quant

182: return “Decremented inventory for “+item+”. Stocked at “+

➥ str(inventory[item])

183:def getInventory():

184: messageString = “\nInventory\n\n”

185: for item in inventory.keys():

186: messageString += “Item:”+item+” quantity: “+ str(inventory[item])+

➥ ”\n”

187: return messageString

188:con = jabber.Component(host=’localhost’, debug=0, port=7002, log=’log’)

189:try:

190: clients = pickle.load(open(‘inv-clients.p’))

191: # for each string create a new InventoryClient and then send a message say-ing ‘we’re back’

192: for c in clients.keys():

Listing 5.16 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 205

Page 223: Jabber Developer’s Handbook [Sams 2004]

206 Extending the Jabber Server

193: print “Client :”+str(c)

194: clients[c] = InventoryClient(c, con, myFromID)

195:except IOError, e:

196: print(e)

197: clients = {}

198:try: inventory = pickle.load(open(‘inv.p’))

199:except IOError, e:

200: print(e)

201: inventory = {}

202:try:

203: con.connect()

204:except IOError, e:

205: print “Couldn’t connect: %s” % e

206: sys.exit(0)

207:else:

208: print “Connected”

209:

210:con.process(1)

211:if con.auth(‘secret’):

212: print “connected”

213:else:

214: print “problems with handshake: “, con.lastErr, con.lastErrCode

215: sys.exit(1)

216:con.setMessageHandler(messageCB)

217:con.setPresenceHandler(presenceCB)

218:con.setIqHandler(iqCB)

219:p = jabber.Presence(type=’available’)

220:p.setFrom(myFromID+’/registered’)

221:for c in clients.keys():

222: p.setTo(clients[c].jid)

223: con.send(p)

224:WAITSECONDS = 300

225:try:

226: while(1):

227: con.process(WAITSECONDS)

228: for c in clients.keys():

229: clients[c].process()

230:

231:except KeyboardInterrupt:

232: p = jabber.Presence(type=’unavailable’)

233: p.setFrom(myFromID+’/registered’)

Listing 5.16 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 206

Page 224: Jabber Developer’s Handbook [Sams 2004]

207An Inventory Management Service

234: for c in clients.keys():

235: p.setTo(clients[c].jid)

236: con.send(p)

237: clients[c] = 0

238: pickle.dump(inventory, open(‘inv.p’, ‘w’))

239: pickle.dump(clients, open(‘inv-clients.p’, ‘w’))

240: con.disconnect()

Listing 5.17 inventoryClient.py

1:class InventoryClient:

2: True = 1

3: False = 0

4: def __init__(self, jid, connection, serviceFromID):

5: self.online = 0

6: self.status = None

7: self.rapport = None

8: self.jid = jid

9: self.connection = connection

10: self.serviceFromID = serviceFromID

11:

12: def setStatus(self, status):

13: self.status = status

14:

15: def setShow(self, show):

16: self.show = show

17: self.setOnline(1)

18: def setOnline(self, online):self.online = online

19: def isOnline(self): return(self.online)

20: def isAvailable(self):

21: if (self.show == None) or (self.show == ‘chat’):

22: return(True)

23: else:

24: return(False)

25: def process(self):

26: if self.rapport == None:

27: self.rapport = ‘established’

33: self.rapport = ‘established’

Listing 5.16 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 207

Page 225: Jabber Developer’s Handbook [Sams 2004]

208 Extending the Jabber Server

Decomposing the Inventory ManagementServiceFirst, we import the necessary libraries as usual. Notice that we import the Pythonimplementation of NIST’s Secure Hash Algorithm (SHA), which we’ll use to provideencryption for data gathered from the client. (There’s nothing sensitive in this particularapplication, but some other application might request a login/password pair, for exam-ple.) The jabber_iq:register get and set packets are handled on lines 21–69.Alsonotice that we import the pickle library, which we will use to read (lines 215 and 223)and write (lines 266 and 267) the client list and the inventory from persistent store. Online 7 we import the InventoryClient class.

1:import jabber

2:import sys

3:import sha

4:import pickle

5:import string

6:import sys, re, os, string

7:from inventoryClient import InventoryClient

8:keys = {}

9:clients = {}

Here we specify the JabberID of this application so that it can appear as a contact in aroster.

10:myFromID = ‘stockist’

This application’s vocabulary recognizes words delimited by spaces and any number ofcharacters within a set of parentheses.These characters will be the lookup key for inven-tory items.We are expecting to see something like <operator> (<inventory item>)<quantity> or <operator> in the conversation from the client.We distill the vocabularyinto a regular expression on line 11.

11:re_item = re.compile(“\W(.*)\W”)

We define a set of global operations to handle callbacks for the Jabber <iq> protocol(line 13,) Jabber <presence> protocol (line 90), and message protocol (line 140).

When the client sends a service registration message as a result of the dialogue shownin Figure 5.13, an <iq> message is routed to the service:

<iq id=’jcl_37’ to=’stockist’ type=’get’ from=’caitlin@localhost/Exodus’>

<query xmlns=’jabber:iq:register’/>

</iq>

07 0672325365 CH05 1/21/05 2:24 PM Page 208

Page 226: Jabber Developer’s Handbook [Sams 2004]

209Decomposing the Inventory Management Service

In the iqCB method, we manipulate the to and from addresses, then handle this getpacket by setting up a reply using an iq set packet, which will be used to elicit registra-tion information from the client:

13:def iqCB(con, iq):

...

20: query_ns = iq.getQuery()

21: if query_ns == jabber.NS_REGISTER:

22: if type == ‘get’:

24: resultIq.setType(‘result’)

25: iq_from = str(iq.getFrom())

26: keys[iq_from] = sha.new(iq_from)

We create an encryption key and stow it with the sender’s ID as the hash ID (line 26).Then we create a packet containing the registration instructions in the same manner asthe earlier Java example:

<iq to=’caitlin@localhost/Exodus’ from=’stockist’ id=’jcl_37’ type=’result’>

<query xmlns = ‘jabber:iq:register’ >

<key>6163c927016ae60f7bea9651042232ad6a227cb7</key>

<instructions>

Welcome to the inventory manager. Command set:

(l)ist :: show inventory

(p)ut (item-descr) quantity :: add inventory. please note required

➥ use of ‘()’

(t)ake (item-descr) quantity :: decr inventory. please note required

➥ use of ‘()’

</instructions>

</query>

</iq>

These instructions are rendered at the client as shown earlier in Figure 5.14.The jabber.py library uses the information in the fields dictionary to populate the<instructions> tag and the <key> tag (lines 38–41). Finally, the iq response is sent (line 43).

28: fields = {

29: ‘instructions’:

30: “””

31:Welcome to the inventory manager. Command set:

32:(l)ist :: show inventory

33:(p)ut (item-descr) quantity :: add inventory. please note required

➥ use of ‘()’

34:(t)ake (item-descr) quantity :: decr inventory. please note required

➥ use of ‘()’

35: “””,

36: ‘key’: keys[iq_from].hexdigest()

37: }

07 0672325365 CH05 1/21/05 2:24 PM Page 209

Page 227: Jabber Developer’s Handbook [Sams 2004]

210 Extending the Jabber Server

38: for field in fields.keys():

39: field_node = query_result.insertTag(field)

40: if fields[field]:

41: field_node.putData(fields[field])

43: con.send(resultIq)

The callback also handles the response from the client, which looks like the XMLfragment shown in the following example.The response contains a key matching the onesent by the service; not total protection against spoofing, but some at least.As you saw inthe previous examples, if the client subscribing to the service had returned any responso-rial information, it would have been contained in key/value pairs matching those in thepacket sent out by the service.

<iq id=’jcl_38’ to=’stockist’ type=’set’ from=’caitlin@localhost/Exodus’>

<query xmlns=’jabber:iq:register’>

<key>6163c927016ae60f7bea9651042232ad6a227cb7</key>

</query>

</iq>

The service checks out the key to validate it, handles error conditions, and finallysubscribes to the client’s presence (lines 59–61).The service also adds the client to itsinternal list (line 57), which the KeyboardInterrupt handler will serialize by using thePython pickle mechanism should the service terminate.

44: elif type == ‘set’:

45: client_key_node = iq.getQueryNode().getTag(‘key’)

46: if not client_key_node:

47: resultIq.setType(‘error’)

48: resultIq.setError(‘no key given!’)

49: con.send(resultIq)

50: else:

51: if keys[str(iq.getFrom())].hexdigest() ==

➥ client_key_node.getData():

52: if iq.getQueryNode().getTag(‘remove’):

53: del clients[iq.getFrom().getStripped()]

54: else:

55: jid = iq.getFrom().getStripped()

56: client = InventoryClient(jid, con, myFromID)

57: clients[client.jid] = client

59: sub_req = jabber.Presence(iq.getFrom(),

➥ type=’subscribe’)

60: sub_req.setFrom(str(iq.getTo()) + “/registered”)

61: con.send(sub_req)

Lines 90–139 (shown earlier) handle the various presence messages forwarded by thejabberd on behalf of clients.This application is not particularly sophisticated in its han-dling, treating presence indications as more or less informational. Other applications

07 0672325365 CH05 1/21/05 2:24 PM Page 210

Page 228: Jabber Developer’s Handbook [Sams 2004]

211Decomposing the Inventory Management Service

might use the information to start or stop billing or make use of the information inother ways, so it’s worthwhile to consider other creative uses of this information channel.

There’s nothing exceptional in the messageCB code; it simply delegates the parse ofthe message body to a helper routine and turns a response around to reply to the user. Ifthe message from the client is within the vocabulary of the acceptable semantic, then theservice produces a response whose side effects include updating the inventory; otherwiseit “punts,” saying Sorry, Command not understood.

Line 188 shows the creation of a new instance of a Jabber connection.

188:con = jabber.Component(host=’localhost’, debug=0, port=7002, log=’log’)

Don’t forget that the parameters for this connection must agree with the appropriatestanzas in the jabber.xml configuration file. Remember that there are two affected stan-zas; the first is the <browse> stanza, which populates a client’s information about theavailable services and is the starting point for any client to attach to a service.

<browse>

<service id=”stockist” name=”Inventory Manager”>

<ns>jabber:iq:register</ns>

<ns>jabber:iq:gateway</ns>

</service>

</browse>

You also need to add a stanza defining the service’s parameters somewhere later injabber.xml:

<service id=”stockist”>

<accept>

<ip/>

<secret>secret</secret>

<port>7002</port>

</accept>

</service>

Notice that this information corresponds to the instance creation information on line188 of the Python code shown in Listing 5.16. Obviously, because (in this example) weare provisioning the application on our localhost, we use that value in the constructorand a port number of 7002.

We next open the pickled client list from a previous run.When we pickled the dic-tionary keyed by the client JID in the previous run (line 231–239), we replaced theinstance of InventoryClient object with junk (an integer 0, line 237). It made no senseto serialize and store an entire Python class—it essentially became meaningless when theapplication halted.When the application restarts, the pickle file is read into the dictionary(line 190) and a new instance of the InventoryClient is substituted. If the file doesn’texist or is unusable, then we just create an empty dictionary (line 197).We do a similarthing with the inventory dictionary, whose key is the inventory description, with eachvalue being an integer representing the amount on hand (lines 198–201).

07 0672325365 CH05 1/21/05 2:24 PM Page 211

Page 229: Jabber Developer’s Handbook [Sams 2004]

212 Extending the Jabber Server

189:try:

190: clients = pickle.load(open(‘inv-clients.p’))

192: for c in clients.keys():

194: clients[c] = InventoryClient(c, con, myFromID)

195:except IOError, e:

196: print(e)

197: clients = {}

198:try: inventory = pickle.load(open(‘inv.p’))

199:except IOError, e:

200: print(e)

201: inventory = {}

Line 202, ahead, shows the connection being made to the running jabberd,

TIPYou might want to do something more sophisticated in the try/except during the connection to jabberd,

such as set a timeout and retry loop; for simplicity’s sake we simply assume the jabberd is running and fail

if we can’t connect.

We connect to the jabberd here and then set the callback for the message types.

202:try:

203: con.connect()

204:except IOError, e:

205: print “Couldn’t connect: %s” % e

206: sys.exit(0)

207:else:

208: print “Connected”

216:con.setMessageHandler(messageCB)

217:con.setPresenceHandler(presenceCB)

218:con.setIqHandler(iqCB)

219:p = jabber.Presence(type=’available’)

220:p.setFrom(myFromID+’/registered’)

221:for c in clients.keys():

222: p.setTo(clients[c].jid)

223: con.send(p)

224:WAITSECONDS = 300

225:try:

226: while(1):

227: con.process(WAITSECONDS)

228: for c in clients.keys():

229: clients[c].process()

230:

231:except KeyboardInterrupt:

07 0672325365 CH05 1/21/05 2:24 PM Page 212

Page 230: Jabber Developer’s Handbook [Sams 2004]

213Decomposing the Inventory Management Service

232: p = jabber.Presence(type=’unavailable’)

233: p.setFrom(myFromID+’/registered’)

234: for c in clients.keys():

235: p.setTo(clients[c].jid)

236: con.send(p)

237: clients[c] = 0

238: pickle.dump(inventory, open(‘inv.p’, ‘w’))

239: pickle.dump(clients, open(‘inv-clients.p’, ‘w’))

240: con.disconnect()

The code for the client’s proxy to the service is pretty small.There’s not a lot to do,really:

1:class InventoryClient:

The constructor populates properties from the invoking application shown in Listing5.16, and the methods handle responses to changes in presence sent from the clients.

4: def __init__(self, jid, connection, serviceFromID):

5: self.online = 0

6: self.status = None

7: self.rapport = None

8: self.jid = jid

9: self.connection = connection

10: self.serviceFromID = serviceFromID

11:

12: def setStatus(self, status):

13: self.status = status

14:

15: def setShow(self, show):

16: self.show = show

17: self.setOnline(1)

18: def setOnline(self, online):self.online = online

19: def isOnline(self): return(self.online)

20: def isAvailable(self):

21: if (self.show == None) or (self.show == ‘chat’):

22: return(True)

23: else:

24: return(False)

07 0672325365 CH05 1/21/05 2:24 PM Page 213

Page 231: Jabber Developer’s Handbook [Sams 2004]

214 Extending the Jabber Server

Process() is called by the invoker to perform any post-constructor actions:

25: def process(self):

26: if self.rapport == None:

27: self.rapport = ‘established’

This pattern works well for this type of application.To illustrate this, here’s anInternet headline reader cast in the same mold.There are subtle differences in what wepickle between sessions, and in the information needed to set up the client, but take alook at the user experience and by all means try the code.

This service accesses Internet news sites and whenever a new headline is found (thatis, one that has not yet been displayed to clients), the service opens a chat window anddisplays the headline as the content.The service does this by keeping a list of clients(implemented as a dictionary—see Listing 5.18, ahead, lines 152–155).When the serviceexits (as a result of a Ctrl+C, for example,) the client list and an array of headlines previ-ously sent is pickled.When the service is restored, so is the client list.

Once again, a user can choose the service, in this case the Headline Checker (againrefer to Figure 5.12).This time, using Register with a Service from the client’s menu, wetype in the name of the service (headliner).

Figure 5.17 Registering with the service.

Notice that this dialogue is different and tailored to the service’s information needs. Herethe fields dictionary specifies that it will be looking for the user to supply a host and aresource, (see Listing 5.18, ahead, lines 27–32).These get reflected in the UI as instruc-tions in Figure 5.18.The fields we solicit are shown in Figure 5.19.The resulting roster isshown in Figure 5.20.

We have typed in slashdot.org for the site and slashdot.rss as the XML formatprovider resource.You can choose others.There are numerous RSS format providers outthere.

Once again, the service appears as a roster item.The user does nothing additional to interact with the service.When it has output to

provide to the user, it will initiate a new chat window.The user can leave the chat win-dow up, in which case the service will simply append new output, minimize the win-dow, and alert the screen (on the Windows toolbar, for example). Or the service willclose the chat window, in which case the service will send a new chat message, whichwill result in the client raising a new chat window, as shown in Figure 5.21

07 0672325365 CH05 1/21/05 2:24 PM Page 214

Page 232: Jabber Developer’s Handbook [Sams 2004]

215Decomposing the Inventory Management Service

Figure 5.18 The service “wizard” (1).

Figure 5.19 The service “wizard” (2).

Figure 5.20 The service as roster item.

07 0672325365 CH05 1/21/05 2:24 PM Page 215

Page 233: Jabber Developer’s Handbook [Sams 2004]

216 Extending the Jabber Server

Figure 5.21 Chatting with the service.

So that’s the user experience; rather different from the Inventory Manager, but noticehow much code it replicates from that example. First, as we’ve done before, here’s thecode in its entirety, and then we’ll comment on the more interesting bits of it:

Listing 5.18 rssComponent.py

1:import jabber

2:import sys

3:import sha

4:import pickle

5:import string

6:import httplib

7:import sys, re, os, string

8:from rssClient import RSSClient

9:from rssAccount import RSSAccount

10:keys = {}

11:myFromID = ‘headliner’

12:def iqCB(con, iq):

13: resultIq = jabber.Iq(to=iq.getFrom())

14: resultIq.setID(iq.getID())

15: resultIq.setFrom(iq.getTo())

16: myFromID = iq.getTo()

17: rssHost = ‘rssHost’

18: rssResource = ‘rssResource’

19: query_result = resultIq.setQuery(iq.getQuery())

20: type = iq.getType()

21: query_ns = iq.getQuery()

22: if query_ns == jabber.NS_REGISTER:

23: if type == ‘get’:

24: resultIq.setType(‘result’)

25: iq_from = str(iq.getFrom())

07 0672325365 CH05 1/21/05 2:24 PM Page 216

Page 234: Jabber Developer’s Handbook [Sams 2004]

217Decomposing the Inventory Management Service

26: keys[iq_from] = sha.new(iq_from)

27: fields = {

28: rssHost: ‘slashdot.org’,

29: rssResource: ‘/slashdot.rss’,

30: ‘instructions’: ‘Enter the rss hostname

➥ (example: slashdot.org) and resource on that URL

➥ (example: slashdot.rss)’,

31: ‘key’: keys[iq_from].hexdigest()

32: }

33: for field in fields.keys():

34: field_node = query_result.insertTag(field)

35: if fields[field]:

36: field_node.putData(fields[field])

37: con.send(resultIq)

38: elif type == ‘set’:

39: client_key_node = iq.getQueryNode().getTag(‘key’)

40: if not client_key_node:

41: resultIq.setType(‘error’)

42: resultIq.setError(‘no key given!’)

43: con.send(resultIq)

44: else:

45: if keys[str(iq.getFrom())].hexdigest() == client_key_node.getData():

46: if iq.getQueryNode().getTag(‘remove’):

47: del clients[iq.getFrom().getStripped()]

48: else:

49: jid = iq.getFrom().getStripped()

50: rssAccount = iq.getQueryNode().getTag(rssHost)

51: if rssAccount:

52: rssAccount = str(rssAccount.getData())

53: rssResource = iq.getQueryNode().getTag(rssResource)

54: if rssResource:

55: rssResource = str(rssResource.getData())

56: account = RSSAccount()

57: account.rssAccount = rssAccount

58: account.rssResource = rssResource

59: account.newsItems = {}

60: client = RSSClient(jid, con, myFromID, account)

61: clients[client.jid] = client

62: sub_req = jabber.Presence(iq.getFrom(),

➥ type=’subscribe’)

63: sub_req.setFrom(str(iq.getTo()) + “/registered”)

64: con.send(sub_req)

65: resultIq.setType(‘result’)

66: con.send(resultIq)

67: else:

Listing 5.18 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 217

Page 235: Jabber Developer’s Handbook [Sams 2004]

218 Extending the Jabber Server

68: resultIq.setType(‘error’)

69: resultIq.setError(‘invalid key’, 400)

70: con.send(resultIq)

71: del keys[str(iq.getFrom())]

72: else:

73: print “don’t know how to handle type”, type, “for query”,

➥ query_ns

74:

75: elif (query_ns == jabber.NS_AGENT) and (type == ‘get’):

76: # someone wants information about us

77: resultIq.setType(‘result’)

78: responses = {

79: ‘name’: “RSS Headline Checker Component”,

80: # ‘url’: None,

81: ‘description’: “this is the RSS Headline Checker Component”,

82: ‘transport’: “<future>...”,

83: ‘register’: None, # we can be registered with

84: ‘service’: ‘test’ # nothing really standardized here...

85: }

86: for response in responses.keys():

87: resp_node = query_result.insertTag(response)

88: if responses[response]:

89: resp_node.putData(responses[response])

90: con.send(resultIq)

91: else:

92: print “don’t know how to handle type”, type, “for query”,

➥ query_ns

93:def presenceCB(con, pres):

94: p = jabber.Presence(to=pres.getFrom())

95: p.setFrom(pres.getTo())

96:

97: type = pres.getType()

98: if str(pres.getFrom().getStripped()) in clients.keys():

99: client = clients[pres.getFrom().getStripped()]

100: else:

101: print(“not able to find client for “ + pres.getFrom().getStripped())

102:

103: client = None

104: if type != ‘unsubscribed’:

105: type = ‘unsubscribe’

106:

107: if not type:

108: type = ‘available’

109: print(pres.getFrom().getStripped() + “ is “ + type)

Listing 5.18 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 218

Page 236: Jabber Developer’s Handbook [Sams 2004]

219Decomposing the Inventory Management Service

110: if type == ‘unavailable’:

111: client.setOnline(0)

112: p.setType(‘unavailable’)

113: con.send(p)

114:

115: elif type == ‘subscribe’:

116: p.setType(‘subscribed’)

117: con.send(p)

118: p.setType(‘subscribe’)

119: con.send(p)

120: elif type == ‘unsubscribe’:

121: p.setType(‘unsubscribed’)

122: con.send(p)

123: p.setType(‘unsubscribe’)

124: con.send(p)

125:

126: elif type == ‘unsubscribed’:

127: pass

128: elif type == ‘probe’:

129: p.setType(‘available’)

130: con.send(p)

131:

132: elif type == ‘available’:

133: # user is online

134: client.setStatus(pres.getStatus())

135: client.setShow(pres.getShow())

136: p.setType(‘available’)

137: con.send(p)

138:def messageCB(con, msg):

139: m = jabber.Message()

140: m.setFrom(msg.getTo())

141: m.setTo(msg.getFrom())

142: m.setBody(“OK :”+ msg.getBody())

143: type = msg.getType()

144: if type == None: type = ‘normal’

145: if type == ‘chat’ or type == ‘normal’:

146: jid = str(msg.getFrom())

147: message = “INFO: Message from “ + jid + “:” + msg.getBody()

148: print message

149: con.send(m)

150:con = jabber.Component(host=’localhost’, debug=0, port=7001, log=’log’)

Listing 5.18 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 219

Page 237: Jabber Developer’s Handbook [Sams 2004]

220 Extending the Jabber Server

151:try:

152: clients = pickle.load(open(‘rss-clients.p’))

153: for c in clients.keys():

154: account = clients[c]

155: clients[c] = RSSClient(c, con, myFromID, account)

156:except IOError, e:

157: print(e)

158: clients = {}

159:

160:

161:try:

162: con.connect()

163:except IOError, e:

164: print “Couldn’t connect: %s” % e

165: sys.exit(0)

166:else:

167: print “Connected”

168:

169:con.process(1)

170:if con.auth(‘secret’):

171: print “connected”

172:else:

173: print “problems with handshake: “, con.lastErr, con.lastErrCode

174: sys.exit(1)

175:con.setMessageHandler(messageCB)

176:con.setPresenceHandler(presenceCB)

177:con.setIqHandler(iqCB)

178:p = jabber.Presence(type=’available’)

179:p.setFrom(myFromID+’/registered’)

180:for c in clients.keys():

181: p.setTo(clients[c].jid)

182: con.send(p)

183:WAITSECONDS = 300

184:try:

185: while(1):

186: con.process(WAITSECONDS)

187: for c in clients.keys():

188: clients[c].process()

189:

190:except KeyboardInterrupt:

191: p = jabber.Presence(type=’unavailable’)

192: p.setFrom(myFromID+’/registered’)

193: for c in clients.keys():

194: p.setTo(clients[c].jid)

Listing 5.18 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 220

Page 238: Jabber Developer’s Handbook [Sams 2004]

221Decomposing the Inventory Management Service

195: con.send(p)

196: tmpClient = clients[c]

197: account = tmpClient.account

198: clients[c] = account

199: pickle.dump(clients, open(‘rss-clients.p’, ‘w’))

200: con.disconnect()

On lines 8 and 9, we import the capabilities shown in Listings 5.19, rssClient.py, and5.20, rssAccount.py, which are shown later in the chapter.

8:from rssClient import RSSClient

9:from rssAccount import RSSAccount

On line 11, we use a unique identifier (‘headliner’) for the service:

11:myFromID = ‘headliner’

We respond to registration requests in much the same way as we did in the previousexample.The get message generates a response to populate the client-side wizard GUI,shown earlier in Figures 5.18 and 5.19.

12:def iqCB(con, iq):

13: resultIq = jabber.Iq(to=iq.getFrom())

14: resultIq.setID(iq.getID())

15: resultIq.setFrom(iq.getTo())

16: myFromID = iq.getTo()

17: rssHost = ‘rssHost’

18: rssResource = ‘rssResource’

19: query_result = resultIq.setQuery(iq.getQuery())

20: type = iq.getType()

21: query_ns = iq.getQuery()

22: if query_ns == jabber.NS_REGISTER:

23: if type == ‘get’:

24: resultIq.setType(‘result’)

25: iq_from = str(iq.getFrom())

26: keys[iq_from] = sha.new(iq_from)

Notice that the wizard (Figure 5.19) has prepopulated the text entry areas withslashdot.org and /slashdot.rss because we set things up that way (at lines 28 and29).You can change these to suit your taste (or leave them blank—specify empty stringsas the values for rssHost and rssResource, respectively. Notice that we insert the<rssHost/> and <rssResource/> into the response as supplied to the client by the user:

33: for field in fields.keys():

34: field_node = query_result.insertTag(field)

35: if fields[field]:

36: field_node.putData(fields[field])

37: con.send(resultIq)

Listing 5.18 Continued

07 0672325365 CH05 1/21/05 2:24 PM Page 221

Page 239: Jabber Developer’s Handbook [Sams 2004]

222 Extending the Jabber Server

NOTEAs we mentioned in the discussion of Listing 5.8, the Jabber protocol does not (at this writing) allow either

the <rssHost/> or <rssResource/> tag within a jabber:iq:register packet. In general,

clients and servers are happy to ignore tag constructs they don’t understand. The Python library ignores the

embedded tags, as does the current version of the Jabber server, but future, less lenient versions of either of

these might. One solution to this problem would be to use one of the approved tags (<misc>, for exam-

ple), rather than <rssHost/> and <rssResource/>.

We create an rssAccount object and set its properties (lines 56–59).

56: account = RSSAccount()

57: account.rssAccount = rssAccount

58: account.rssResource = rssResource

59: account.newsItems = {}

We create a client proxy on the server and pass it the client’s JID, the connection tothe client, the server’s identity, and the rssAcount.

60: client = RSSClient(jid, con, myFromID, account)

61: clients[client.jid] = client

62: sub_req = jabber.Presence(iq.getFrom(),

➥ type=’subscribe’)

63: sub_req.setFrom(str(iq.getTo()) + “/registered”)

64: con.send(sub_req)

Note the port and host specification (line 150) for the connection object, and don’tforget that the parameters for this connection must agree with the appropriate stanzas inthe jabber.xml configuration file.

150:con = jabber.Component(host=’localhost’, debug=0, port=7001, log=’log’)

On line 183, the WAITSECONDS parameter controls the frequency of checking theInternet resource.You might want to tailor this to your own taste.

We replace the rssClient instance with persistable account details and then dumpthat to persistent store (lines 193–199).

193: for c in clients.keys():

194: p.setTo(clients[c].jid)

195: con.send(p)

196: tmpClient = clients[c]

197: account = tmpClient.account

198: clients[c] = account

199: pickle.dump(clients, open(‘rss-clients.p’, ‘w’))

200: con.disconnect()

07 0672325365 CH05 1/21/05 2:24 PM Page 222

Page 240: Jabber Developer’s Handbook [Sams 2004]

223Decomposing the Inventory Management Service

This code implements the client proxy, which in turn holds the account informationfor the resource being monitored. It does the mundane functional stuff to poll theInternet service (stealing cycles from the client’s computer) and ensures that previouslyread headlines are not displayed.You might want to do something fancier about makingsure that the buffer doesn’t get too big (emptying it at midnight, for example), but wedidn’t. In Listing 5.19, we show the client imported on line 8 of Listing 5.18, and usedon line 60 of Listing 5.18 to objectify the client.

Listing 5.19 rssClient.py

1:import httplib

2:import jabber

3:import sys, re, os, string

4:from rssAccount import RSSAccount

5:class RSSClient:

6: True = 1

7: False = 0

8: Delivered = ‘Delivered’

9: def __init__(self, jid, connection, serviceFromID, account):

10: self.online = 0

11: self.status = None

12: self.jid = jid

13: self.connection = connection

14: self.serviceFromID = serviceFromID

15: self.account = RSSAccount()

16: self.account.rssAccount = account.rssAccount

17: self.account.rssResource = account.rssResource

18: self.account.newsItems = account.newsItems

19: self.re_title = re.compile (“<title>”)

20: self.re_title_end = re.compile (“</title>”)

21: self.re_desc = re.compile (“<description>”)

22: self.re_desc_end = re.compile (“</description>”)

23: def setStatus(self, status):

24: “””Set the client’s “verbose” status”””

25: self.status = status

26: def setShow(self, show):

27: “””Set the client’s show mode; one of away, chat, xa, dnd, or ‘None’”””

28: # http://docs.jabber.org/jpg/html/main.html#REFSHOW

29: # One of away, chat, xa, or dnd.

30: self.show = show

31: self.setOnline(1)

32: def setOnline(self, online):

33: “””Set whether the user is online or not”””

34: self.online = online

35: def isOnline(self):

36: “””Return the state of the user’s online’ness”””

37: return(self.online)

38: def isAvailable(self):

39: “””Return a Boolean based on the user’s show status”””

07 0672325365 CH05 1/21/05 2:24 PM Page 223

Page 241: Jabber Developer’s Handbook [Sams 2004]

224 Extending the Jabber Server

In the constructor, we populate the rssAccount’s properties (lines 15–19) and set up theregular expression matchers to parse the XML returned by the service being queried. Inthe RSS format, headlines are contained between <title>...</title> pairs and thecontent is contained between <description>...</description> pairs.

9: def __init__(self, jid, connection, serviceFromID, account):

10: self.online = 0

11: self.status = None

12: self.jid = jid

13: self.connection = connection

14: self.serviceFromID = serviceFromID

15: self.account = RSSAccount()

16: self.account.rssAccount = account.rssAccount

17: self.account.rssResource = account.rssResource

18: self.account.newsItems = account.newsItems

19: self.re_title = re.compile (“<title>”)

20: self.re_title_end = re.compile (“</title>”)

21: self.re_desc = re.compile (“<description>”)

22: self.re_desc_end = re.compile (“</description>”)

Finally, we broke out the details of the account into something that could be easilypersisted into the pickle store, separating out the interesting bits that need saving fromsession to session (see Listing 5.20).

Listing 5.20 rssAccount.py

1:class RSSAccount:

2:

3: def __init__(self):

4: self.rssAccount = “”

5: self.rssResource = “”

6: self.newsItems = {}

The objective of this example was to give you a little foreshadowing of what networkservices could be like with Jabber at their core. In the next major section of the book(“Jabber-based Networked Applications”), we will describe the concepts of Jabber mes-sage-based computing and how they enable a new style of distributed applications; thoseyou might previously have considered to be candidates for enterprise Java or for sophisti-cated and expensive Windows development and deployment tools.

SummaryIn this chapter we have shown the mechanisms for extending the jabberd server to addnew capabilities beyond simple instant messaging.Although there is some complexityinvolved, we would suggest that Jabber’s component architecture is far more friendly toadding flexible capabilities than enterprise Java or Microsoft’s .NET architecture, and wehope that the examples have made the case for us. Before we dive into building Web serv-ices with Jabber, let us first take an in-depth look at Jabber security in the next chapter.

07 0672325365 CH05 1/21/05 2:24 PM Page 224

Page 242: Jabber Developer’s Handbook [Sams 2004]

6Jabber Security

Distrust and caution are the parents of security.

Benjamin Franklin

They who would give up an essential liberty for temporary security, deserve neither liberty nor security.

Benjamin Franklin

SECURING AN INFORMATION SYSTEM (like anything else) requires a tradeoff betweenthe amount of effort required to secure the system and the risks associated with notmaking those efforts. One of the strengths of the Jabber protocols and its open-systemsphilosophy is that security measures can be designed, built, and used as appropriate forthe individual application. Contrast this with proprietary systems in which the vendordecides what the security options are and how they can be used.The open Jabber proto-cols and software give us the freedom to apply security in our own way.

Following up on the low-level protocol of user registration and authentication inChapter 3,“Jabber Client Protocol,” this chapter turns to the system-level implications ofJabber security and shows how to take advantage of the security features of the Jabberprotocols, server, and clients.We show how the security needs of a Jabber system-of-systems differ from the needs of an instant chat system and discuss techniques for inter-operability between Jabber and other user authentication and authorization systems.Finally, we present a complete example of a user privilege manager implemented as aJabber server component.

08 0672325365 CH06 1/21/05 2:24 PM Page 225

Page 243: Jabber Developer’s Handbook [Sams 2004]

226 Jabber Security

Client RegistrationWhen you first start your Jabber server, there are no valid users of the system. (Here weuse “user” to mean an interactive client like WinJab or a service implemented as a Jabberclient.) New users must be registered with the server before they can authenticate(login) and use its services.As with all Jabber interactions, there is a sequence of XMLpackets that must be exchanged between the client and the server to register a new user.

Typically, registration occurs before the client authenticates as an existing user—immediately after the exchange of stream headers. Here’s an example of a client request-ing a connection to the server called my-jabber as a client:

First, the client sends its stream header:

<?xml version=’1.0’?>

<stream:stream to=”my-jabber” xmlns=”jabber:client”

xmlns:stream=”http://etherx.jabber.org/streams”>

If all is well, the server replies with its own stream header:

<?xml version=’1.0’?>

<stream:stream xmlns:stream=’http://etherx.jabber.org/streams’

id=’3DE29D5E’ xmlns=’jabber:client’ from=’my-jabber’>

Now that the stream is established, a new user can be registered.A well-behavedclient queries the server to determine what parameters are necessary for registration andto fetch human-readable instructions.This is the IQ packet that the client sends to theserver to request registration information:

<iq id=”sn-0” type=”get”>

<query xmlns=”jabber:iq:register”></query>

</iq>

In this case, the server replies with the fields that it requires the new user to provideand registration instructions.The default server registration component doesn’t enforcethese requirements, though, so as long as the client provides a username and password, itsregistration will be accepted. In fact, most instant messaging clients skip the registrationget step all together. However, we’re playing by the rules, so we’re interested in the serv-er’s reply:

<iq id=”sn-0” type=”result”>

<query xmlns=”jabber:iq:register”>

<instructions>

Choose a username and password to register with this server.

</instructions>

<password/>

<email/>

<username/>

</query>

</iq>

08 0672325365 CH06 1/21/05 2:24 PM Page 226

Page 244: Jabber Developer’s Handbook [Sams 2004]

227Client Registration

Looks like this server is configured to require just a username, a password, and anemail address. In this example, we register a new user with the username user1 with thepassword password and the email address [email protected] using the resourceWork. Here’s the IQ packet that the client sends to the server:

<iq type=”set” id=”JCOM_23”>

<query xmlns=”jabber:iq:register”>

<username>user1</username>

<password>password</password>

<resource>Work</resource>

<email>[email protected]</email>

</query>

</iq>

If the registration request is successful, the server replies with an IQ result packet like this:

<iq type=’result’ id=’JCOM_23’/>

Because the type is ‘result’ and not ‘error’, we can assume success. If there is anerror (a user by that name already exists, for example), the server would return an errorpacket something like this:

<iq type=’error’ id=’JCOM_26’>

<query xmlns=’jabber:iq:register’>

<username>user1</username>

<password>password</password>

<resource>Work</resource>

<email>[email protected]</email>

</query>

<error code=’409’>Username Not Available</error>

</iq>

The error code 409 and the message Username Not Available says that the nameuser1 is already in use.

Listing 6.1 is a small Java program that registers a new user.

Listing 6.1 New User Registration Example

1:package org.jabber;

2:

3:import org.jabber.jabberbeans.*;

4:import org.jabber.jabberbeans.Extension.*;

5:import org.jabber.jabberbeans.util.PacketDebug;

6:import java.net.*;

7:import java.util.*;

8:

9:public class Register implements PacketListener {

10:

08 0672325365 CH06 1/21/05 2:24 PM Page 227

Page 245: Jabber Developer’s Handbook [Sams 2004]

228 Jabber Security

11: ConnectionBean cb;

12: String server_host;

13: String username;

14: String password;

15: final static String getID = “get-01”;

16: final static String setID = “set-01”;

17:

18: public Register(String[] args) {

19: server_host = args[0];

20: username = args[1];

21: password = args[2];

22: cb = new ConnectionBean();

23: cb.addPacketListener(new PacketDebug());

24: cb.addPacketListener(this);

25: try {

26: cb.connect(InetAddress.getByName(server_host));

27: InfoQueryBuilder iqb = new InfoQueryBuilder();

28: IQRegisterBuilder reg = new IQRegisterBuilder();

29: iqb.setType(“get”);

30: iqb.setIdentifier(getID);

31: iqb.addExtension(reg.build());

32: Packet iq = iqb.build();

33: cb.send(iq);

34: } catch (Exception ex) {

35: ex.printStackTrace();

36: }

37: }

38: public void receivedPacket(PacketEvent pkt) {

39: try {

40: if (pkt.getPacket() instanceof InfoQuery) {

41: InfoQuery iq = (InfoQuery) pkt.getPacket();

42: if (iq.getIdentifier().equals(getID)) {

43: Enumeration enum = iq.Extensions();

44: while (enum.hasMoreElements()) {

45: Extension ext = (Extension) enum.nextElement();

46: if (ext instanceof IQRegister) {

47: IQRegister iqr = (IQRegister) ext;

48: Enumeration names = iqr.getNames();

49: System.out.println(

➥ ”Registration requests: “);

50: while (names.hasMoreElements()) {

51: System.out.println(

➥ ” “ + names.nextElement());

52: }

53: InfoQueryBuilder iqb =

Listing 6.1 Continued

08 0672325365 CH06 1/21/05 2:24 PM Page 228

Page 246: Jabber Developer’s Handbook [Sams 2004]

229Client Registration

➥ new InfoQueryBuilder();

54: IQRegisterBuilder irb =

➥ new IQRegisterBuilder();

55: iqb.setType(“set”);

56: iqb.setIdentifier(setID);

57: irb.set(“username”, username);

58: irb.set(“password”, password);

59: iqb.addExtension(irb.build());

60: cb.send(iqb.build());

61: }

62: }

63: } else if (iq.getIdentifier().equals(setID)) {

64: if (iq.getType().equals(“error”)) {

65: System.out.println(

66: “Registration Error:”

67: + iq.getErrorCode()

68: + “:”

69: + iq.getErrorText());

70: } else {

71: System.out.println(“Registration Successful”);

72: }

73: System.exit(0);

74: }

75: }

76: } catch (InstantiationException ex) {

77: ex.printStackTrace();

78: }

79: }

80:

81: /* These two methods are required by the PacketListener interface */

82: public void sendFailed(PacketEvent arg0) {

83: }

84: public void sentPacket(PacketEvent arg0) {

85: }

86:

87: public static void main(String[] args) {

88: if (args.length != 3)

89: System.err.println(

90: “Usage: Register <server> <username> <password>”);

91: else

92: new Register(args);

93: }

94:}

Listing 6.1 Continued

08 0672325365 CH06 1/21/05 2:24 PM Page 229

Page 247: Jabber Developer’s Handbook [Sams 2004]

230 Jabber Security

The meat of this little program starts at line 22.After parsing the command-line argu-ments into the host to which we want to register and the username/password of thenew user, we create a connection to the server using the JabberBeans ConnectionBean atline 22 and initialize it at line 26.At line 23, we add a listener to the connection thatprints each packet as it is received and sent, which is very useful when debugging.Atline 24, we add ourselves as a packet listener so we can see the replies to our queries.Lines 27–32 create the IQ jabber:iq:register get query as in the XML examplesshown earlier, and line 33 sends the packet to the server.

The JabberBeans library calls the receivedPacket() method (line 38) when a packetis received from the server. In this case, it should be the reply to the get request that wesent earlier. First (line 40), we make sure this is an IQ packet. Next (line 42), we makesure this is the response to our get request. If it is, then starting at line 45, we look at itsextensions to see whether there is a jabber:iq:register extension.There’d better be,and we print the elements for informational purposes (like many clients, we don’t actual-ly use them). Finally (starting at line 53), we build an IQ set packet to do the registra-tion and send it to the server. Notice that we explicitly set the identifier (line 56), ratherthan letting JabberBeans generate one, so we can recognize the response.

When the response comes, it is recognized at line 63.We simply check to see whetherit was unsuccessful (line 64) and print the error message text if it failed.

A successful run of this little program looks like this:

sent a packet! :

org.jabber.jabberbeans.XMLStreamHeader

<stream:stream xmlns:stream=”http://etherx.jabber.org/streams”xmlns=”jabber:client” to=”my-jabber”>

sent a packet! :

org.jabber.jabberbeans.InfoQuery

<iq id=”get-01” type=”get”><query xmlns=”jabber:iq:register”></query></iq>

received a packet! :

org.jabber.jabberbeans.XMLStreamHeader

<stream:stream xmlns:stream=”http://etherx.jabber.org/streams”xmlns=”jabber:client” from=”my-jabber” id=”3DE38CA4”>

received a packet! :

org.jabber.jabberbeans.InfoQuery

<iq id=”get-01” type=”result”><queryxmlns=”jabber:iq:register”><instructions>Choose a username and password to regis-ter with thisserver.</instructions><password/><email/><name/><username/></query></iq>

Registration requests:

instructions

password

email

name

username

sent a packet! :

org.jabber.jabberbeans.InfoQuery

<iq id=”set-01” type=”set”><query

08 0672325365 CH06 1/21/05 2:24 PM Page 230

Page 248: Jabber Developer’s Handbook [Sams 2004]

231Client Registration

xmlns=”jabber:iq:register”><username>user1</username><password>password</password></query></iq>

received a packet! :

org.jabber.jabberbeans.InfoQuery

<iq id=”set-01” type=”result”></iq>

Registration Successful

Most of the output here is from the PacketDebug listener, but you can see the outputfrom the receivedPacket() method highlighted in bold.The contents of the last packettell us that the registration was successful. If the user already exists, the last few lines looklike this instead:

sent a packet! :

org.jabber.jabberbeans.InfoQuery

<iq id=”set-01” type=”set”><queryxmlns=”jabber:iq:register”><username>user1</username><password>password</password></query></iq>

received a packet! :

org.jabber.jabberbeans.InfoQuery

<iq id=”set-01” type=”error”><error code=”409”>Username NotAvailable</error><queryxmlns=”jabber:iq:register”><username>user1</username><password>password</password></query></iq>

Registration Error:409:Username Not Available

Notice that the last IQ packet contains an <error> element (highlighted).

Disabling Automatic RegistrationIt’s worth noting that the default configuration of the Jabber server accepts all valid andnon-duplicate registration requests—effectively allowing anyone to use it.This may notbe what you want.You can disable registration of new users by configuring the JSM tonot load the registration module (see Chapter 2,“Installing and Configuring JabberSoftware,” for more configuration details). Simply find the section of jabber.xml thatloads the JSM components and comment out mod_register like this:

<load main=”jsm”>

<jsm>./libs/jsm.dll</jsm>

<mod_echo>./libs/jsm.dll</mod_echo>

<mod_roster>./libs/jsm.dll</mod_roster>

<mod_time>./libs/jsm.dll</mod_time>

<mod_vcard>./libs/jsm.dll</mod_vcard>

<mod_last>./libs/jsm.dll</mod_last>

<mod_version>./libs/jsm.dll</mod_version>

<mod_announce>./libs/jsm.dll</mod_announce>

<mod_agents>./libs/jsm.dll</mod_agents>

<mod_browse>./libs/jsm.dll</mod_browse>

<mod_admin>./libs/jsm.dll</mod_admin>

<mod_filter>./libs/jsm.dll</mod_filter>

08 0672325365 CH06 1/21/05 2:24 PM Page 231

Page 249: Jabber Developer’s Handbook [Sams 2004]

232 Jabber Security

<mod_offline>./libs/jsm.dll</mod_offline>

<mod_presence>./libs/jsm.dll</mod_presence>

<mod_auth_plain>./libs/jsm.dll</mod_auth_plain>

<mod_auth_digest>./libs/jsm.dll</mod_auth_digest>

<mod_auth_0k>./libs/jsm.dll</mod_auth_0k>

<mod_log>./libs/jsm.dll</mod_log>

<!— <mod_register>./libs/jsm.dll</mod_register> —>

<mod_xml>./libs/jsm.dll</mod_xml>

</load>

This will cause the server to reject all registration requests.

Registration DataOkay, what if I disable arbitrary registration requests—how can I create new users? Oneway would be to temporarily enable registration and use a program, such as the oneyou’ve already seen, to create a bunch of new users. However, because the Jabber serverstores its registration data as simple XML documents, it’s possible to generate new usersoffline. Here’s the XML document that was created in the server’s ./spool directory in afile called user1.xml after the client registered the user1 account:

<xdb>

<query xmlns=’jabber:iq:last’ last=’1038322852’ xdbns=’jabber:iq:last’>

Registered

</query>

<password xmlns=’jabber:iq:auth’ xdbns=’jabber:iq:auth’>

password

</password>

<query xmlns=’jabber:iq:register’ xdbns=’jabber:iq:register’>

<username>

user1

</username>

<password xmlns=’jabber:iq:auth’>

password

</password>

<x xmlns=’jabber:x:delay’ stamp=’20021126T15:00:52’>

registered

</x>

</query>

</xdb>

As you can see, it’s mostly just the registration data that we provided and some time-stamp information. Copy this file to user2.xml, edit it to change the <username> touser2, and restart the server. Presto! New user. So it would be easy to write a script thatwent through a database or an employee list and created one of these files for each user.

08 0672325365 CH06 1/21/05 2:24 PM Page 232

Page 250: Jabber Developer’s Handbook [Sams 2004]

233Client Authentication

Client AuthenticationBefore the Jabber server will provide services to any connection, the connection must beauthenticated as a valid system user.Again, a “user” could represent a person or a sys-tem—they look the same to the server.The Jabber server has three built-in methods ofauthenticating users: plain authentication, digest authentication, and zero-knowledgeauthentication.These three methods differ in the data that the client and the server passback and forth during the authentication interaction. In this section, we look at each oneand also write one of our own.

Client authentication is handled by the JSM service and the three authenticationmethods are configured in the jabber.xml file in the JSM section shown here.The threeauthentication modules are highlighted in bold:

<load main=”jsm”>

<jsm>./libs/jsm.dll</jsm>

<mod_echo>./libs/jsm.dll</mod_echo>

<mod_roster>./libs/jsm.dll</mod_roster>

<mod_time>./libs/jsm.dll</mod_time>

<mod_vcard>./libs/jsm.dll</mod_vcard>

<mod_last>./libs/jsm.dll</mod_last>

<mod_version>./libs/jsm.dll</mod_version>

<mod_announce>./libs/jsm.dll</mod_announce>

<mod_agents>./libs/jsm.dll</mod_agents>

<mod_browse>./libs/jsm.dll</mod_browse>

<mod_admin>./libs/jsm.dll</mod_admin>

<mod_filter>./libs/jsm.dll</mod_filter>

<mod_offline>./libs/jsm.dll</mod_offline>

<mod_presence>./libs/jsm.dll</mod_presence>

<mod_auth_plain>./libs/jsm.dll</mod_auth_plain>

<mod_auth_digest>./libs/jsm.dll</mod_auth_digest>

<mod_auth_0k>./libs/jsm.dll</mod_auth_0k>

<mod_log>./libs/jsm.dll</mod_log>

<mod_register>./libs/jsm.dll</mod_register>

<mod_xml>./libs/jsm.dll</mod_xml>

</load>

You can comment out any of the authentication methods that you don’t want to support.

CAUTIONIn the 1.4.2 version of jabberd, if you remove the mod_auth_plain JSM module, registration of new

users doesn’t work.

08 0672325365 CH06 1/21/05 2:24 PM Page 233

Page 251: Jabber Developer’s Handbook [Sams 2004]

234 Jabber Security

In all cases, the authentication begins with the client sending an IQ get request usingthe namespace jabber:iq:auth and its username.The document for user1 looks like this:

<iq type=”get” id=”JCOM_4”>

<query xmlns=”jabber:iq:auth”>

<username>

user1

</username>

</query>

</iq>

This tells the server that user1 would like to log in and requests the list of supportedauthentication methods. If the server is configured with all three forms of authentication,it responds with a packet like this:

<iq type=’result’ id=’JCOM_4’>

<query xmlns=’jabber:iq:auth’>

<username>

user1

</username>

<password/>

<digest/>

<sequence>

494

</sequence>

<token>

3DE39085

</token>

<resource/>

</query>

</iq>

The list of supported authentication methods is provided in the elements within thejabber:iq:auth query element.The presence of certain elements in this packet tells theclient that the server supports authentication types plain (<password/>), digest(<digest/>), and zero-knowledge (<sequence> and <token>), and also tells the clientthat it should supply a resource to log in (<resource>).The client can choose any of thethree authentication methods.

Before looking in detail at the three authentication methods, Listing 6.2 shows a smallabstract Java class that demonstrates how to authenticate to the Jabber server.The abstractmethod authenticate() is implemented three different ways in Listings 6.3 through 6.5.

08 0672325365 CH06 1/21/05 2:24 PM Page 234

Page 252: Jabber Developer’s Handbook [Sams 2004]

235Client Authentication

Listing 6.2 Authentication Base Class

1:package org.jabber;

2:

3:import org.jabber.jabberbeans.*;

4:import org.jabber.jabberbeans.Extension.*;

5:import java.net.*;

6:import java.util.*;

7:

8:public abstract class AuthBase implements PacketListener {

9: ConnectionBean cb;

10: String server_host;

11: String username;

12: String password;

13: String resource;

14:

15: public AuthBase(String[] args) {

16: server_host = args[0];

17: username = args[1];

18: password = args[2];

19: resource = args[3];

20: cb = new ConnectionBean();

21: cb.addPacketListener(this);

22: try {

23: cb.connect(InetAddress.getByName(server_host));

24: InfoQueryBuilder iqb = new InfoQueryBuilder();

25: IQAuthBuilder iqa = new IQAuthBuilder();

26: iqa.setUsername(username);

27: iqb.setType(“get”);

28: iqb.addExtension(iqa.build());

29: Packet iq = iqb.build();

30: cb.send(iq);

31: } catch (Exception ex) {

32: ex.printStackTrace();

34: }

35:

36: String authMsgId = null;

37: public void receivedPacket(PacketEvent pkt) {

38: if (pkt.getPacket() instanceof InfoQuery) {

39: InfoQuery iq = (InfoQuery) pkt.getPacket();

40: if (iq.getIdentifier().equals(authMsgId)) {

41: if (iq.getErrorCode() == null)

42: System.out.println(“Successfully authenticated”);

43: else

44: System.out.println(

45: “Authentication failed: “ + iq.getErrorText());

46: cb.disconnect();

08 0672325365 CH06 1/21/05 2:24 PM Page 235

Page 253: Jabber Developer’s Handbook [Sams 2004]

236 Jabber Security

47: System.exit(0);

48: }

49: Enumeration enum = iq.Extensions();

50: while (enum.hasMoreElements()) {

51: Extension ext = (Extension) enum.nextElement();

52: if (ext instanceof IQAuth) {

53: authMsgId = authenticate((IQAuth) ext);

54: }

55: }

56: }

57: }

58:

59: protected abstract String authenticate(IQAuth ext);

60:

61: /* These methods are required by the PacketListener interface */

62: public void sendFailed(PacketEvent arg0) {}

63: public void sentPacket(PacketEvent arg0) {}

64:}

The constructor (starting at line 15) parses the command line arguments into the serverhost, username, password, and resource to use for authentication. Lines 20–23 create aconnection to the server and set up this object as a packet listener on the connection.Lines 24–30 create and send an IQ get packet, as in the example XML that preceedsListing 6.2.

When a response is received, the receivedPacket() method is called. Lines 40–48check to see whether the packet is the jabber:iq:auth set response and, if so, printwhether the authentication was successful or not and exit.The set IQ request for whichit’s looking is sent by the abstract method authenticate() at line 53. It’s up to the threeinheriting classes to implement authenticate() and demonstrate the three authentica-tion methods.

Plain AuthenticationPlain authentication is the simplest form of authentication and the easiest to understandand implement. In plain authentication, the client simply passes its username and pass-word in plaintext (unencrypted) in a packet like this:

<iq type=”set” id=”JCOM_25”>

<query xmlns=”jabber:iq:auth”>

<username>

user1

</username>

<password>

password

</password>

Listing 6.2 Continued

08 0672325365 CH06 1/21/05 2:24 PM Page 236

Page 254: Jabber Developer’s Handbook [Sams 2004]

237Client Authentication

<resource>

Work

</resource>

</query>

</iq>

Here the user authenticating is user1 and its password is simply password.The serverreplies with a result packet like this if the authentication is successful:

<iq type=’result’ id=’JCOM_25’/>

If authentication fails for some reason, the server replies with an error IQ packet thatlooks like this:

<iq type=’error’ id=’JCOM_25’>

<query xmlns=’jabber:iq:auth’>

<username>

user1

</username>

<password>

bad-password

</password>

<resource>

Work

</resource>

</query>

<error code=’401’>

Unauthorized

</error>

</iq>

Here the error code 401 means that the authentication failed for some reason. Notethat the Jabber server returns the same error code for a wrong password as it does for anon-existent user.This makes it harder for potential hackers to guess valid usernamesthan if it returned a packet that said I know this user, but the password iswrong.

The example in Listing 6.3 extends the base class shown in Listing 6.2 above todemonstrate plain authentication.

Listing 6.3 Plain Authentication

1:package org.jabber;

2:

3:import org.jabber.jabberbeans.*;

4:import org.jabber.jabberbeans.Extension.*;

5:

6:public class AuthPlain extends AuthBase {

7:

8: public AuthPlain(String[] args) {

08 0672325365 CH06 1/21/05 2:24 PM Page 237

Page 255: Jabber Developer’s Handbook [Sams 2004]

238 Jabber Security

9: super(args);

10: }

11: public static void main(String[] args) {

12: new AuthPlain(args);

13: }

14: protected String authenticate(IQAuth ext) {

15: try {

16: InfoQueryBuilder iqb = new InfoQueryBuilder();

17: IQAuthBuilder iab = new IQAuthBuilder();

18: iqb.setType(“set”);

19: iab.setUsername(username);

20: iab.setPassword(password);

21: iab.setResource(resource);

22: iqb.addExtension(iab.build());

23: InfoQuery myiq = (InfoQuery) iqb.build();

24: String authMsgId = myiq.getIdentifier();

25: cb.send(myiq);

26: return authMsgId;

27: } catch (InstantiationException ex) {

28: ex.printStackTrace();

29: }

30: return null;

31: }

32:}

The authenticate() method (starting at line 14) implements the plain authentication.It simply creates an IQ set packet with a jabber:iq:auth extension (line 17) and fillsin the username, password, and resource tags. Pretty simple.

Although plain authentication is simple to implement, it has some serious short-comings. Most obviously, the password is transmitted in the clear (unencrypted) from theclient to the server.Anyone with access to the network between the two computers caneasily learn the password. Less obvious is the risk posed by the implementation of themod_auth_plain component, which stores the unencrypted password in the XDB data-base.There it (and every other user’s password) is vulnerable to anyone who can getaccess to the XDB database. It’s unfortunate that mod_auth_plain doesn’t store a crypto-graphic hash of the password rather than the password itself.We’ll see more about cryp-tographic hashes in the next sections.

Digest AuthenticationA digest is another name for a cryptographic hash.A digest algorithm has the followingproperties:

n The same input data always produces the same output data.The output is usually afixed size and smaller than the expected input data (hence a “digest”).

Listing 6.3 Continued

08 0672325365 CH06 1/21/05 2:24 PM Page 238

Page 256: Jabber Developer’s Handbook [Sams 2004]

239Client Authentication

n A small change in the input data has a large and unpredictable effect on the output data.

n It is unlikely that two inputs will produce the same output.n The input data cannot be deduced from the output data. Sometimes these algo-

rithms are called “one-way” algorithms, because it’s easy to compute the outputgiven the input, but hard to compute the input given the output.

There are several common hash algorithms and Jabber uses one called SHA-1, which iswidely available and considered quite strong.These algorithms are very useful for han-dling passwords because you can make a hash of a password and then give the hash tosomeone else who can verify whether you actually know the password without youactually telling them the password.

Take Figure 6.1 as an example. Suppose Alice and Bob share a password, and Bobneeds to verify whether Alice knows the correct password.

Figure 6.1 Password exchange via digest.

If Alice just tells Bob the password, then he can tell whether she knows it or not, but ifBob didn’t really know the password—oops, he does now. Using digests avoids this prob-lem;Alice and Bob independently hash their passwords and exchange the hashes.Thisway neither actually has to give the password to the other.

����� ���

���� � �������

��� �������� ��� ��������

���� � �������

����

��������� �� �� ���

��� ���� ���

����� ���� ���

����

08 0672325365 CH06 1/21/05 2:24 PM Page 239

Page 257: Jabber Developer’s Handbook [Sams 2004]

240 Jabber Security

There needs to be a little more to this, however.After Alice gives Bob the hash of thepassword, that’s really all he needs to exchange passwords with anyone else.They need toadd some more data to the password before they hash it, so they can be sure that theother party actually carried out the hash and didn’t just learn the hash result somewhereelse. Figure 6.2 shows how this works.

Figure 6.2 Better password exchange via digest.

Here Alice and Bob agree on some random data to append to the password before hash-ing it.This ensures that the hash output is different every time.

The Jabber server uses this method to do digest authentication. Under digest authenti-cation, the client appends the password to the session ID (from the stream header) andcreates a hash of this string to use for authentication.The session ID is serving as the ran-dom addition to the hash input function as “1234” did in Figure 6.2.As a reminder, thestream header from the server looks something like this (the stream ID is highlighted):

<stream:stream xmlns:stream=’http://etherx.jabber.org/streams’

id=’3DE3AC8F’ xmlns=’jabber:client’ from=’my-jabber’>

����� ���

���� � �������

������ ����������� ������ �����������

��� ����� !� ��� ����� !�

���� � �������

����

��"���#�� �� � ���

��� $��� ���

����� $��� ���

����

�%��� �� ������

08 0672325365 CH06 1/21/05 2:24 PM Page 240

Page 258: Jabber Developer’s Handbook [Sams 2004]

241Client Authentication

If <digest/> is present in the reply to the jabber:iq:auth get query, the clientknows that the server supports digest authentication, appends the password to the streamID, hashes the result (3DE3AC8Fpassword, in this case), and sends a packet like this, whichincludes a <digest> element—shown in bold—rather than a <password> element usedin plain authentication:

<iq type=”set” id=”JCOM_31”>

<query xmlns=”jabber:iq:auth”>

<username>

user1

</username>

<digest>

30118fe0f80503fe4beb8277f797b7318942a07d

</digest>

<resource>

Work

</resource>

</query>

</iq>

The server does the same, and if its hash matches the client’s hash, then the client issuccessfully authenticated.

Listing 6.4 shows the Java code to implement digest authentication with the lines thatdiffer from plain authentication highlighted in bold.

Listing 6.4 Digest Authentication

1:package org.jabber;

2:

3:import org.jabber.jabberbeans.*;

4:import org.jabber.jabberbeans.Extension.*;

5:import org.jabber.jabberbeans.util.*;

6:

7:public class AuthDigest extends AuthBase {

8: public AuthDigest(String[] args) {

9: super(args);

10: }

11: public static void main(String[] args) {

12: new AuthDigest(args);

13: }

14:

15: protected String authenticate(IQAuth ext) {

16: try {

17: SHA1Helper sha1helper = new SHA1Helper();

18: String sessionID = cb.getSessionID();

19: String digest = sha1helper.digest(sessionID, password);

20: IQAuth iqa = (IQAuth) ext;

08 0672325365 CH06 1/21/05 2:24 PM Page 241

Page 259: Jabber Developer’s Handbook [Sams 2004]

242 Jabber Security

21: InfoQueryBuilder iqb = new InfoQueryBuilder();

22: IQAuthBuilder iab = new IQAuthBuilder();

23: iqb.setType(“set”);

24: iab.setUsername(username);

25: iab.setSHA1Digest(digest);

26: iab.setResource(resource);

27: iqb.addExtension(iab.build());

28: InfoQuery myiq = (InfoQuery) iqb.build();

29: String authMsgId = myiq.getIdentifier();

30: cb.send(myiq);

31: return authMsgId;

32: } catch (InstantiationException ex) {

33: ex.printStackTrace();

34: }

35: return null;

36: }

37:}

The authenticate() method in this class (lines 15–36), uses a helper class to calculatethe SHA-1 hash.The SHA1Helper class is part of the JabberBeans library and an instance of this class is created at line 17.At line 18, we get the stream ID from theConnectionBean, which stored it when it parsed the stream header. Line 19 calculates thehash (digest) of the stream ID and password; then it’s simply a matter of assembling an IQpacket with a jabber:iq:auth extension, adding the <digest> tag (line 25), and sendingit. If the password is correct, we should get the no-error response from the server.

Digest authentication is an improvement over plain authorization, because now some-one listening to the interchange over the network can’t learn the password. He could seethe digest value, but it’s valid for only this session, so it’s not too useful. However, thismethod still has the problem that the password must be stored on the server, and it isstored unencrypted in the XDB database.

Zero-Knowledge AuthenticationZero-knowledge authentication attempts to improve on the other types of authentica-tion by not requiring the server to store the clients’ passwords.

When a client registers with the server, the mod_auth_0k module in the JSM takesthe password and a random string and hashes them together just as the digest authentica-tion does when it’s authenticating a client. It goes further, though, and takes that resultand hashes it again, takes that result and hashes it again, and so on, some number of timesand stores the result of the iterative hashing, along with the number of times it hashedand the random string. It doesn’t need to store the password itself—only the client needsto know the password.

Listing 6.4 Continued

08 0672325365 CH06 1/21/05 2:24 PM Page 242

Page 260: Jabber Developer’s Handbook [Sams 2004]

243Client Authentication

Then, when a client wants to authenticate, it asks the server for the random stringand the number of times it hashed, combines the string and the password, and performsthe same iterative hashing operation, but stops one iteration short. So, if the server’s itera-tion count is 400, the client performs 399 hashes and sends the result to the server.

The server can then perform one more hash on the data and see whether it matcheswhat it has stored. If it does, then authentication was successful and the server canremember the client’s hash value and decrement the iteration counter for the next time.The next time this client authenticates, it will do the iterative hash 398 times and theserver will do the 399th.

These data elements are encoded in the XML exchanged between the client and theserver.As you recall, here’s an example of the XML packet that the server sends theclient in response to a jabber:iq:auth get request with the zero-knowledge data high-lighted:

<iq type=’result’ id=’JCOM_4’>

<query xmlns=’jabber:iq:auth’>

<username>

user1

</username>

<password/>

<digest/>

<sequence>

494

</sequence>

<token>

3DE39085

</token>

<resource/>

</query>

</iq>

The parts of interest for zero-knowledge authentication are highlighted in bold.The<sequence> element (494 here) is the count of the number of times the server hashashed, and <token> (3DE39085 here) is the random string that the server added to thepassword before hashing it when the password was registered.

The client takes these two values and the password, hashes 493 times, and comes upwith an authentication packet, like this:

<iq type=”set” id=”JCOM_4”>

<query xmlns=”jabber:iq:auth”>

<username>

user1

</username>

<hash>

9a9bf2b4f453c52aa584874c17cdc83a2d3f1652

</hash>

08 0672325365 CH06 1/21/05 2:24 PM Page 243

Page 261: Jabber Developer’s Handbook [Sams 2004]

244 Jabber Security

<resource>

Work

</resource>

</query>

</iq>

The result of the iterative hashing is in the highlighted <hash> tag.The server takesthis value, hashes it one more time, and if it matches its stored value, authentication issuccessful; the server stores the hash value provided by the client, decrements thesequence counter, and responds with a no-error result packet. Listing 6.5 shows theauthenticate() method that performs zero-knowledge authentication.

Listing 6.5 Zero-Knowledge Authentication

1: protected String authenticate(IQAuth ext) {

2: try {

3: SHA1Helper sha1helper = new SHA1Helper();

4: String token = ext.getZeroKToken();

5: int sequence = ext.getZeroKSequence();

6: String hashedPassword = sha1helper.digest(password, null);

7: String hash = sha1helper.digest(hashedPassword, token);

8: while (sequence— > 0) {

9: hash = sha1helper.digest(hash,null);

10: }

11: IQAuth iqa = (IQAuth) ext;

12: InfoQueryBuilder iqb = new InfoQueryBuilder();

13: IQAuthBuilder iab = new IQAuthBuilder();

14: iqb.setType(“set”);

15: iab.setUsername(username);

16: iab.setZeroKHash(hash);

17: iab.setResource(resource);

18: iqb.addExtension(iab.build());

19: InfoQuery myiq = (InfoQuery) iqb.build();

20: String authMsgId = myiq.getIdentifier();

21: cb.send(myiq);

22: return authMsgId;

23: } catch (InstantiationException ex) {

24: ex.printStackTrace();

25: }

26: return null;

27: }

The zero-knowledge data from the server is processed at lines 4 and 5, and the zero-knowledge hash is added to the response packet at line 16.

Although zero-knowledge has the advantage of not requiring the transmission orstorage of the password, its disadvantage is that you get only a limited number of logins

08 0672325365 CH06 1/21/05 2:24 PM Page 244

Page 262: Jabber Developer’s Handbook [Sams 2004]

245A Custom Authentication Component

(until the sequence counter reaches 1) before the password must be reset.This is a goodexample of trading some convenience for security.

CAUTIONEven if you disable mod_auth_plain and mod_auth_digest, the 1.4.2 server will still store the

password in the XDB database. Other parts of the JSM depend on a <password> entry to determine

whether a username is valid. If you’re really concerned about storing passwords on the server, you can stop

the server, edit the user XML files, and replace the password text with some bogus text like “XXX.” Zero-

knowledge authentication will still work.

A Custom Authentication ComponentIn this section, we implement our own server component to do customized authentica-tion. Let’s suppose that we want to manage our clients’ passwords outside the XDB data-base.We want to have a list of passwords for each client, and when the client logs inusing one password, the next time we require the next one in the list. So if the client’spassword list looks like this:

bluebird

helloworld

artichoke

The first login will use bluebird as the password, the second will use helloworld,and the third will use artichoke.After that, we’ll have to distribute a new set of pass-words before the client can successfully authenticate again.This is the similar to the zero-knowledge authentication method described earlier, except we’re choosing the passwordsrather than generating them with SHA-1.You’ll see later in this section that this tech-nique could be adapted to use other password sources, such as a corporate directoryservice, for example.

We configure the Jabber server to use our component for authentication by editingthe jabber.xml file. First, we add our new authenticator as a service that will connect tothe server on a network port by adding this section to the main section of jabber.xml(that is, not inside the JSM service tag):

<service id=”myauth.my-jabber”>

<accept>

<ip/>

<port>9002/port>

<secret>secret</secret>

</accept>

</service>

This tells the Jabber server that a service called myauth.my-jabber will connect onport 9002 and use the password secret to authenticate. Services use a modified form ofdigest authentication when they connect to the service, which you’ll see in the followingsection.

08 0672325365 CH06 1/21/05 2:24 PM Page 245

Page 263: Jabber Developer’s Handbook [Sams 2004]

246 Jabber Security

Now we need to tell the JSM to route authentication requests to the new service.Todo this, we add a new tag in the JSM configuration section of jabber.xml, inside the<jsm> tag, that looks like this:

<auth>myauth.my-jabber</auth>

The contents of this element should match the ID of the service defined in the<service> configuration element.This will cause all registration and authenticationrequests (IQ packets bearing the jabber:iq:register and jabber:iq:auth name-spaces) to be routed to the myauth.my-jabber component.

Connecting the Authentication ServiceBefore the authentication service can authenticate clients, it must authenticate itself tothe Jabber server.There is a special protocol for authenticating services that is similar tothe digest client authentication. It starts with the connecting service sending a streamheader packet to the server. Here’s an example:

<stream:stream xmlns:stream=”http://etherx.jabber.org/streams”

xmlns=”jabber:component:accept” to=”my-jabber”

from=”myauth” id=”jdkjdf8sd9”>

Notice that the XML namespace is jabber:component:accept, rather than jabber:client as it is for client connections.Also, unlike the client connection, theconnecting service sends the server a session ID.

The server responds with a matching stream header of its own that looks like this:

<stream:stream xmlns:stream=”http://etherx.jabber.org/streams”

xmlns=”jabber:component:accept” from=”myauth.my-jabber” id=”3DE3D81A”>

The server uses the same XML namespace and adds its own session ID.The next thing that happens is the service authentication handshake.The handshake

value is the SHA-1 digest of the server’s session ID with the shared secret appended toit. So if, as in this XML stream example, the server’s session ID is 3DE3D81A and theshared secret is secret, the handshake value is the SHA-1 hash of 3DE3D81Asecret.Toinitiate the handshake, the connecting service sends a special handshake packet contain-ing the digest like this:

<handshake>965ebc5956daee3da839c1f2a19d64d5087d3689</handshake>

The server computes the same handshake value and compares it to the one in thehandshake packet. If they match, the service is authenticated and the server respondswith a packet like this:

<handshake></handshake>

If they don’t match, the server responds with an error packet like this:

<stream:error>Invalid handshake</stream:error>

and closes the connection.

08 0672325365 CH06 1/21/05 2:24 PM Page 246

Page 264: Jabber Developer’s Handbook [Sams 2004]

247A Custom Authentication Component

Handling Authorization Packets So what do these authorization packets look like? The IQ packets that are sent from theclient are wrapped in a <route> packet before being delivered to the component.As yousaw in Chapter 4,“Jabber Server Architecture,” the server uses route packets to routemessages internally between its components. By the time it gets to our authenticationcomponent, an IQ get request looks like this:

<route to=”myauth.my-jabber” from=”17@c2s/8145B28” type=”auth”>

<iq id=”JCOM_0” type=”get”>

<query xmlns=”jabber:iq:auth”>

<username>

user1

</username>

</query>

</iq>

</route>

The contents of the route packet tell us that this was originally sent from the c2s(client-to-server) component. Inside the route wrapper is the IQ packet that the clientoriginally sent.We need to reply via the c2s component with a wrapped IQ responsepacket, like this:

<route to=”17@c2s/8145B28” from=”myauth.my-jabber” id=”sn-0” type=”auth”>

<iq id=”JCOM_0” type=”result”>

<query xmlns=”jabber:iq:auth”>

<username>

user1

</username>

<password/>

</query>

</iq>

</route>

As you saw earlier, because this packet includes only the <password/> tag and not the<digest>, <sequence>, or <token> tags, this tells the client that we support only plain(password-only) authentication.The client can then send the server an IQ setjabber:iq:auth request, which will be routed to the authentication service like this:

<route to=”myauth.my-jabber” from=”17@c2s/8145B28” type=”auth”>

<iq id=”JCOM_1” type=”set”>

<query xmlns=”jabber:iq:auth”>

<username>

user1

</username>

<password>

password

</password>

08 0672325365 CH06 1/21/05 2:24 PM Page 247

Page 265: Jabber Developer’s Handbook [Sams 2004]

248 Jabber Security

<resource>

Work

</resource>

</query>

</iq>

</route>

This is just the IQ set message as sent by the client wrapped in a route packet.At this point, the authentication service can decide whether to grant the client access

to the server or not. If the client credentials match, we can reply with a packet like this:

<route to=”17@c2s/8145B28” from=”myauth.my-jabber” id=”sn-2” type=”auth”>

<iq id=”JCOM_1” type=”result”>

<query xmlns=”jabber:iq:auth”>

<username>

user1

</username>

<password>

password

</password>

<resource>

Work

</resource>

</query>

</iq>

</route>

This creates a session in the JSM for this user. If the credentials don’t match, we replywith an error packet like this (notice the highlighted error element):

<route to=”17@c2s/8145B28” from=”myauth.my-jabber” id=”sn-2” type=”auth”>

<iq id=”JCOM_1” type=”error”>

<error code=”401”>

Unauthorized

</error>

<query xmlns=”jabber:iq:auth”>

<username>

user1

</username>

<password>

password

</password>

<resource>

Work

</resource>

</query>

</iq>

</route>

08 0672325365 CH06 1/21/05 2:24 PM Page 248

Page 266: Jabber Developer’s Handbook [Sams 2004]

249A Custom Authentication Component

Either way, the wrapped IQ packet gets routed through the c2s component and backto the client.

Here’s the Java code for the authentication component. It’s fairly long, so we’ll list thewhole thing here and then go through it step by step.

Listing 6.6 A Custom Authentication Service

1:package org.jabber;

2:

3:import java.io.*;

4:import java.net.*;

5:import java.util.*;

6:import org.jabber.jabberbeans.*;

7:import org.jabber.jabberbeans.Extension.*;

8:import org.jabber.jabberbeans.serverside.*;

9:import org.jabber.jabberbeans.util.*;

10:

11:public class AuthService implements PacketListener {

12: private static final String BASE_ACCEPT_XMLNS =

➥ ”jabber:component:accept”;

13: private static final String sessionID = “arbitrary”;

14: private static final String service_name = “myauth”;

15:

16: ConnectionBean cb;

17: String server_host;

18: int port;

19: String secret;

20: public AuthService(String[] args) {

21: server_host = args[0];

22: port = Integer.parseInt(args[1]);

23: secret = args[2];

24: cb = new ConnectionBean();

25: try {

26: setup();

27: } catch (Exception ex) {

28: ex.printStackTrace();

29: }

30: }

31:

32: private void setup()

33: throws

34: IOException,

35: InstantiationException,

36: UnknownHostException,

37: InterruptedException {

38: cb.addPacketListener(new PacketDebug());

39: SyncPacketListener sync = new SyncPacketListener(cb);

08 0672325365 CH06 1/21/05 2:24 PM Page 249

Page 267: Jabber Developer’s Handbook [Sams 2004]

250 Jabber Security

40: Packet p;

41: SHA1Helper sha = new SHA1Helper();

42:

43: cb.disableStreamHeader();

44: cb.connect(InetAddress.getByName(server_host), port);

45:

46: XMLStreamHeaderBuilder xsbuilder = new XMLStreamHeaderBuilder();

47: xsbuilder.setXMLNS(BASE_ACCEPT_XMLNS);

48: xsbuilder.setIdentifier(sessionID);

49: xsbuilder.setFromAddress(new JID(service_name));

50: xsbuilder.setToAddress(new JID(server_host));

51: Packet xmlpacket = xsbuilder.build();

52:

53: // Send the XML stream header

54: synchronized (sync) {

55: cb.send(xmlpacket);

56: p = sync.waitForType(xmlpacket, 5000);

57: }

58:

59: String sessionID = cb.getSessionID();

60: Handshake handshake = new Handshake(sha.digest(sessionID, secret));

61:

62: // Send the handshake

63: p = null;

64: sync.reset();

65: synchronized (sync) {

66: cb.send(handshake);

67: p = sync.waitForType(handshake, 5000);

68: }

69: // Start servicing packets

70: cb.addPacketListener(this);

71: }

72:

73: /*

74: * Called when any packet is received

75: */

76: public void receivedPacket(PacketEvent packetEvent) {

77: Packet packet = packetEvent.getPacket();

78: if (packet instanceof Route) {

79: Route route = (Route) packet;

80: Enumeration extensions = route.Extensions();

81: while (extensions.hasMoreElements()) {

82: Extension ext = (Extension) extensions.nextElement();

83: if (ext instanceof InfoQuery) {

84: InfoQuery iq = (InfoQuery) ext;

Listing 6.6 Continued

08 0672325365 CH06 1/21/05 2:24 PM Page 250

Page 268: Jabber Developer’s Handbook [Sams 2004]

251A Custom Authentication Component

85: Enumeration iqexts = iq.Extensions();

86: while (iqexts.hasMoreElements()) {

87: Extension iqext = (Extension)

➥ iqexts.nextElement();

88: if (iqext instanceof IQAuth) {

89: IQAuth iqa = (IQAuth) iqext;

90: if (iq.getType().equals(“get”)) {

91: replyWithLogin(route, iq, iqa);

92: } else {

93: login(route, iq, iqa);

94: }

95: } else if (iqext instanceof IQRegister) {

96: IQRegister iqr = (IQRegister) iqext;

97: replyWithError(route, iq, iqr);

98: }

99: }

100: }

101: }

102: }

103: }

104:

105: /*

106: * Respond to “get” queries. We do only plain authentication

107: */

108: private void replyWithLogin(Route route, InfoQuery iq, IQAuth iqa) {

109: try {

110: RouteBuilder rb = new RouteBuilder();

111: InfoQueryBuilder iqb = new InfoQueryBuilder();

112: IQAuthBuilder iab = new IQAuthBuilder();

113: iab.setPassword(“”);

114: iab.setUsername(iqa.getUsername());

115: iqb.addExtension(iab.build());

116: rb.setFromAddress(route.getToAddress());

117: rb.setToAddress(route.getFromAddress());

118: rb.setType(“auth”);

119: iqb.setType(“result”);

120: iqb.setIdentifier(iq.getIdentifier());

121: rb.addPacket(iqb.build());

122: cb.send(rb.build());

123: } catch (InstantiationException ex) {

124: ex.printStackTrace();

125: }

126: }

127:

128: /*

Listing 6.6 Continued

08 0672325365 CH06 1/21/05 2:24 PM Page 251

Page 269: Jabber Developer’s Handbook [Sams 2004]

252 Jabber Security

129: * Respond to “set” queries. Do plain authentication

130: */

131: private void login(Route route, InfoQuery iq, IQAuth iqa) {

132: try {

133: RouteBuilder rb = new RouteBuilder();

134: InfoQueryBuilder iqb = new InfoQueryBuilder();

135: rb.setFromAddress(route.getToAddress());

136: rb.setToAddress(route.getFromAddress());

137: rb.setType(“auth”);

138: iqb.setIdentifier(iq.getIdentifier());

139: if (isValid(iqa.getUsername(), iqa.getPassword())) {

140: iqb.setType(“result”); // OK

141: } else {

142: iqb.setType(“error”); // not OK

143: iqb.setErrorCode(“401”);

144: iqb.setErrorText(“Unauthorized”);

145: }

146:

147: iqb.addExtension(iqa);

148: rb.addPacket(iqb.build());

149: cb.send(rb.build());

150: } catch (InstantiationException ex) {

151: ex.printStackTrace();

152: }

153: }

154:

155: /*

156: * Actually read the password file and compare it to the one given.

157: * Passwords are in <username>.pad

158: * Current password counter is in <username>.cnt

159: */

160: private boolean isValid(String username, String password) {

161: boolean ret = false;

162: String realPassword = null;

163: RandomAccessFile padFile = null;

164: int passwordCount = 1;

165: try {

166: padFile = new RandomAccessFile(username + “.pad”, “r”);

167: } catch (FileNotFoundException fnfex) {

168: return false; // no password file for this user

169: }

170: RandomAccessFile counterFile = null;

171: try {

172: File counter = new File(username + “.cnt”);

173: boolean cntExists = counter.exists();

Listing 6.6 Continued

08 0672325365 CH06 1/21/05 2:24 PM Page 252

Page 270: Jabber Developer’s Handbook [Sams 2004]

253A Custom Authentication Component

174: counterFile = new RandomAccessFile(counter, “rw”);

175: if (cntExists) {

176: String cntString = counterFile.readLine();

177: cntString = cntString.trim();

178: passwordCount = Integer.parseInt(cntString);

179: counterFile.seek(0);

180: }

181: for (int i = 0; i < passwordCount; i++) {

182: realPassword = padFile.readLine();

183: }

184: if (realPassword == null) { // end-of-file

185: log(username + “ is out of passwords”);

186: } else if (realPassword.equals(password)) { // OK!

187: counterFile.writeChars(Integer.toString(passwordCount + 1));

188: ret = true;

189: }

190: } catch (Exception ex) {

191: ex.printStackTrace();

192: } finally {

193: if (counterFile != null)

194: try {

195: counterFile.close();

196: } catch (IOException oh_well) {

197: }

198: }

199: return ret;

200: }

201:

202: /*

203: * We don’t handle registration requests. Report the error.

204: */

205: private void replyWithError(Route route, InfoQuery iq, IQRegister iqr) {

206: try {

207: RouteBuilder rb = new RouteBuilder();

208: InfoQueryBuilder iqb = new InfoQueryBuilder();

209: rb.setFromAddress(route.getToAddress());

210: rb.setToAddress(route.getFromAddress());

211: rb.setType(“auth”);

212: iqb.setIdentifier(iq.getIdentifier());

213: iqb.setType(“error”); // not ok

214: iqb.setErrorCode(“403”);

215: iqb.setErrorText(“Forbidden”);

216: iqb.addExtension(iqr);

217: rb.addPacket(iqb.build());

218: cb.send(rb.build());

Listing 6.6 Continued

08 0672325365 CH06 1/21/05 2:24 PM Page 253

Page 271: Jabber Developer’s Handbook [Sams 2004]

254 Jabber Security

219: } catch (InstantiationException ex) {

220: ex.printStackTrace();

221: }

222: }

223:

224: /*

225: * Write a warning message to the log service.

226: */

227: private void log(String msg) {

228: LogBuilder lb = new LogBuilder();

229: lb.setType(“warn”);

230: lb.setFromAddress(new JID(service_name));

231: lb.setContent(msg);

232: cb.send(lb.build());

233: }

234:

235: // Extra methods required by PacketListner interface

236: public void sendFailed(PacketEvent packetEvent) {}

237: public void sentPacket(PacketEvent packetEvent) {}

238:

239: public static void main(String[] args) {

240: new AuthService(args);

241: }

242:}

Let’s take this a piece at a time.After the imports and other Java housekeeping, the realaction starts in the setup() method at line 32.As you saw before, at line 38 we create aPacketDebug and add it as a listener to the ConnectionBean so we can see the packets asthey come in and out.We also create a SyncPacketListener, which lets us synchro-nously wait for particular types of packets to arrive.

Because this is a service connection and not a client connection, we need to disablethe automatic generation of the client stream header in the ConnectionBean with thecall to cb.disableStreamHeader() at line 43. Lines 46–51 set up the stream header asshown in the XML stream header example shown earlier in this section.Then we sendthe packet and wait up to five seconds (5000 milliseconds) for the response at lines54–57.

Next, (lines 59 and 60) calculate the handshake by getting the session ID from theconnection bean and hashing it with the secret using the SHA1Helper.Then, lines 63–68send the handshake and wait for the response. Line 70 adds this object as a packet listen-er to the connection bean so that we start receiving packets at the receivedPacket()method.

Listing 6.6 Continued

08 0672325365 CH06 1/21/05 2:24 PM Page 254

Page 272: Jabber Developer’s Handbook [Sams 2004]

255A Custom Authentication Component

The receivedPacket() method (lines 76–103) follows setup() and is not nearly ascomplicated as it looks. It simply takes each route packet, looks inside it, and dispatches itin one of these four ways:

n Authorization get requests go to replyWithLogin().n Authorization set requests go to login().n Registration requests go to replyWithError().n Everything else is ignored.

Now for the important parts.We need to reply appropriately to jabber:iq:auth get

queries so clients know that they should just send us passwords and not hash themsomehow.The replyWithLogin() method (repeated here so you don’t have to keep flip-ping back) builds up a route packet (line 110), which contains an IQ packet (line 111),which contains an auth extension (line 112).

108: private void replyWithLogin(Route route, InfoQuery iq, IQAuth iqa) {

109: try {

110: RouteBuilder rb = new RouteBuilder();

111: InfoQueryBuilder iqb = new InfoQueryBuilder();

112: IQAuthBuilder iab = new IQAuthBuilder();

113: iab.setPassword(“”);

114: iab.setUsername(iqa.getUsername());

115: iqb.addExtension(iab.build());

116: rb.setFromAddress(route.getToAddress());

117: rb.setToAddress(route.getFromAddress());

118: rb.setType(“auth”);

119: iqb.setType(“result”);

120: iqb.setIdentifier(iq.getIdentifier());

121: rb.addPacket(iqb.build());

122: cb.send(rb.build());

123: } catch (InstantiationException ex) {

124: ex.printStackTrace();

125: }

126: }

Lines 113–121 assemble the various parts of the reply into a packet that looks likethis:

<route to=”17@c2s/81471C0” from=”myauth.my-jabber” id=”sn-0” type=”auth”>

<iq id=”JCOM_6” type=”result”>

<query xmlns=”jabber:iq:auth”>

<username>user1</username>

<password/>

</query>

</iq>

</route>

08 0672325365 CH06 1/21/05 2:24 PM Page 255

Page 273: Jabber Developer’s Handbook [Sams 2004]

256 Jabber Security

The <password/> tells clients that they should authenticate with plain authentication.The login() method is called by receivedPacket() when a client is attempting to

authenticate. It is repeated for convenience here:

131: private void login(Route route, InfoQuery iq, IQAuth iqa) {

132: try {

133: RouteBuilder rb = new RouteBuilder();

134: InfoQueryBuilder iqb = new InfoQueryBuilder();

135: rb.setFromAddress(route.getToAddress());

136: rb.setToAddress(route.getFromAddress());

137: rb.setType(“auth”);

138: iqb.setIdentifier(iq.getIdentifier());

139: if (isValid(iqa.getUsername(), iqa.getPassword())) {

140: iqb.setType(“result”); // OK

141: } else {

142: iqb.setType(“error”); // not OK

143: iqb.setErrorCode(“401”);

144: iqb.setErrorText(“Unauthorized”);

145: }

146:

147: iqb.addExtension(iqa);

148: rb.addPacket(iqb.build());

149: cb.send(rb.build());

150: } catch (InstantiationException ex) {

151: ex.printStackTrace();

152: }

153: }

As in the previous method, we need to build a route packet, which contains an IQ pack-et, which contains an auth extension.We create the route packet and initialize it with theaddresses and type, and set the identifier on the IQ packet to match the IQ we weregiven (lines 135–138).Then we call isValid() to check the password, and if it is cor-rect, we set the IQ packet’s type to result (that is, no error). If it is not, we set its typeto error and fill in the error code and text. Our task is made a little easier because wejust parrot back the jabber:iq:auth extension that the client gave us (line 147).Thenwe assemble the packet and send it on its way (line 149).

The next method in Listing 6.6 is the isValid() method, which returns true if theusername/password match our records and false if they don’t. It’s not worth goingthrough the ugliness of Java file I/O and text processing; suffice it to say that this methodlooks through the password file for this username, finds the next unused one, and com-pares it to the one given. One interesting thing to note, though, is that if the user runsout of passwords, the service doesn’t tell the user (that’s too much information for apotential attacker), but it does notify the system administrator by creating a log message.It calls the log() method, which is repeated here:

08 0672325365 CH06 1/21/05 2:24 PM Page 256

Page 274: Jabber Developer’s Handbook [Sams 2004]

257A Custom Authentication Component

227: private void log(String msg) {

228: LogBuilder lb = new LogBuilder();

229: lb.setType(“warn”);

230: lb.setFromAddress(new JID(service_name));

231: lb.setContent(msg);

232: cb.send(lb.build());

233: }

Creating log messages is so easy that it makes sense to use them to help the systemoperators.When a user runs out of passwords, this message appears on the console and inthe error.log file:

20021126T20:23:16: [warn] (myauth): user1 is out of passwords

The only part of this service that we haven’t talked about is the replyWithError()method. It is called if we receive a registration request. If new user registration is disabled(as we showed earlier in this chapter), then no registration packets will get to this service,but it’s good to be ready to respond to them anyway. If we get a registration request, theservice responds with a route packet that looks like this:

<route to=”17@c2s/815B3E0” from=”myauth.my-jabber” id=”sn-6” type=”auth”>

<iq id=”JCOM_12” type=”error”>

<error code=”403”>Forbidden</error>

<query xmlns=”jabber:iq:register”>

<password>bluebird</password>

<resource>Work</resource>

<username>user1</username>

</query>

</iq>

</route>

The highlighted <error> element should tell the client that user registration is notallowed.

There are lots of easy changes that could make this service more useful in certain sit-uations. For example,

n Instead of reading passwords from a file, read them from the corporate directoryservice so users can use the same password for their Jabber accounts.

n Store the passwords on the server hashed so that if they are disclosed, they can’t beused to gain access.

n Handle digest authentication rather than plain authentication.n Handle the registration of new users.n Generate a new set of passwords when the old ones run out, encrypt them, and

email them to the user.

08 0672325365 CH06 1/21/05 2:24 PM Page 257

Page 275: Jabber Developer’s Handbook [Sams 2004]

258 Jabber Security

Using SSL for Client ConnectionsThe previous sections showed how authentication is handled between the Jabber serverand its components and clients.Authentication gives the server assurance that it’s talkingto who it thinks it’s talking to, which is one important aspect of security, but not theonly aspect. In this section, we turn to confidentiality, or ensuring that communicationsbetween parties is kept secret from other parties, and integrity, or ensuring that data trans-mitted between parties is not modified in transit.

Fortunately, the Jabber server has built-in support for confidentiality and integritybetween the server and its clients through its support for Secure Sockets Layer (SSL)encryption. Data sent over connections made with SSL are encrypted before being trans-mitted and decrypted by the recipient.This is the same technology that protects secureWeb pages.

SSL OverviewAlthough SSL can be used to provide authentication as well as confidentiality andintegrity, the Jabber server uses it only for confidentiality and integrity and handlesauthentication as described earlier. SSL services use public key certificates to manage theexchange of information between the client and the server.A certificate contains infor-mation about the certified entity (the Jabber server, in our case) and its public crypto-graphic key.The certificate is cryptographically signed so that the recipient can verifythat the key in the certificate really belongs to the certificate’s owner.

NOTEIf you want more details on certificates, SSL, and public key encryption, a great place to start is the book

Applied Cryptography by Bruce Schneier.

The client initiates the connection, which proceeds as shown in Figure 6.3 anddescribed in the following steps:

NOTEFigure 6.3 doesn’t show all the possible SSL messages—only the ones used by the Jabber server.

1. ClientHello. The client sends the SSL version number it supports, a randomnumber (used to create the session key), a session ID, and its set of cipher algo-rithms.

2. ServerHello. The server sends its version number, another random number, thesession ID, and its choice of cipher algorithms from the set that the client supports.

3. Certificate. The server sends its public key certificate so the client can verify itsidentity.

4. ServerHelloDone. The server completes its first part of the handshake.This tellsthe client to not expect any other optional data.

08 0672325365 CH06 1/21/05 2:24 PM Page 258

Page 276: Jabber Developer’s Handbook [Sams 2004]

259Using SSL for Client Connections

Figure 6.3 SSL handshake.

5. ClientKeyExchange. The client sends the server the key material necessary,which varies depending on the cipher algorithm chosen.The key material isencrypted with the server’s public key.

6. ClientChangeCipherSpec. The client notifies the server that it is changing tothe specified cipher algorithm.

7. ClientFinished. The client completes this part of the handshake.

8. ServerChangeCipherSpec. The server notifies the client that it is changing tothe specified cipher algorithm.

9. ServerFinished. The handshake is completed.

10. Jabber Messages. Jabber messages flow across the encrypted channel.

Client Server

ClientHello

ServerHello

Certificate

ServerHelloDone

ClientKeyExchange

ClientChangeCipherSpec

ClientFinished

ServerChangeCipherSpec

ServerFinished

Jabber Messages

08 0672325365 CH06 1/21/05 2:24 PM Page 259

Page 277: Jabber Developer’s Handbook [Sams 2004]

260 Jabber Security

Enabling SSL in the Jabber ServerBefore we can turn on SSL, we need to build jabberd with SSL enabled, and before wecan build the server, we need to make sure that OpenSSL is installed. On a Linuxmachine, we can use the rpm command to see whether it’s installed:

$ rpm -q -a|grep openssl

openssl-devel-0.9.6b-29

openssl-perl-0.9.6b-29

openssl-0.9.6b-29

The openssl-perl package is not required, but the other two are needed to build jab-berd with SSL. Of course, your version numbers may be different than these. If you don’thave these packages, you can get them from http://www.openssl.org.

After OpenSSL is installed, you can configure jabberd to support SSL.You need to re-run the configure command in the Jabber installation directory.

NOTEChapter 2 contains step-by-step instructions for building jabberd. This section describes the changes to that

build process needed to support SSL.

Run the configure command with the argument —enable-ssl and it should look like this:

$ ./configure —enable-ssl

Running Jabber Configure

========================

Searching for SSL.. Found.

Getting pth settings...

A bunch more output… then

Setting Build Parameters.. Done.

Generating Settings Script.. Done.

You may now type ‘make’ to build your new Jabber system.

If configure reports a Not Found error after it prints Searching for SSL..., youmay not have OpenSSL installed correctly.

After configure successfully completes, you can build jabberd by typing make, likethis:

$ make

Making all in pthsock

make[1]: Entering directory ‘/usr/local/jabber-1.4.2/pthsock’

08 0672325365 CH06 1/21/05 2:24 PM Page 260

Page 278: Jabber Developer’s Handbook [Sams 2004]

261Using SSL for Client Connections

gcc -g -Wall -I. -I. -I/usr/include/openssl -DHAVE_SSL

➥ -I/usr/local/jabber-1.4.2/jabberd/pth-1.4.0 -fPIC -I../jabberd/

➥ -c -o client.o client.c

gcc -g -Wall -I. -I. -I/usr/include/openssl -DHAVE_SSL

➥ -I/usr/local/jabber-1.4.2/jabberd/pth-1.4.0 -fPIC -I../jabberd/

➥ -shared -o pthsock_client.so client.o -L/usr/lib -lssl -lcrypto -ldl –lresolv

Notice that the compile commands now contain the include path for the SSL head-ers and references to the SSL crypto libraries.

The server needs a certificate to send to the clients, so we’ll use the OpenSSL tools tobuild one. Run the following command and follow the instructions to create the keysand certificate.You need to provide a passphrase (at least 4 characters) and the X500Distinguished Name that you want to use. Distinguished Names are used to uniquelyidentify certificate holders and are usually based on characteristics such as location andaffiliation. Its contents are not important to the operation of the Jabber server. If youdon’t want to fill in any of the Distinguished Name fields, just enter a dot (.).

$ openssl req -new -x509 -newkey rsa:1024 -days 365

➥ -keyout privkey.pem -out key.pem

Using configuration from /usr/share/ssl/openssl.cnf

Generating a 1024 bit RSA private key

........++++++

...................................++++++

writing new private key to ‘privkey.pem’

Enter PEM pass phrase:****

Verifying password - Enter PEM pass phrase:****

——-

You are about to be asked to enter information that will be incorporated

into your certificate request.

What you are about to enter is what is called a Distinguished Name or a DN.

There are quite a few fields but you can leave some blank

For some fields there will be a default value,

If you enter ‘.’, the field will be left blank.

——-

Country Name (2 letter code) [GB]:US

State or Province Name (full name) [Berkshire]:Virginia

Locality Name (eg, city) [Newbury]:Arlington

Organization Name (eg, company) [My Company Ltd]:SAMS

Organizational Unit Name (eg, section) []:.

Common Name (eg, your name or your server’s hostname) []:my-jabber

Email Address []:.

$

The arguments to this command tell OpenSSL that we want to generate a new (-new) X.509 (-x509) certificate, and that it should generate a new 1024-bit key for usewith the RSA algorithm (-newkey –rsa:1024).The certificate should be valid for one

08 0672325365 CH06 1/21/05 2:24 PM Page 261

Page 279: Jabber Developer’s Handbook [Sams 2004]

262 Jabber Security

year (-days 365), put the private key in privkey.pem and the certificate in key.pem(-keyout privkey.pem -out key.pem).The next command strips out the passphrase soyou don’t have to provide it to the server.You need to give it the passphrase you used inthe previous step.

$ openssl rsa -in privkey.pem -out privkey.pem

read RSA key

Enter PEM pass phrase:****

writing RSA key

$

The Jabber server expects to find the private key appended to the certificate file, sowe just concatenate the certificate and private key and remove the private key file:

cat privkey.pem >> key.pem

rm privkey.pem

Now the certificate and key are in the file key.pem. Naturally, the certificate fileneeds to be protected or someone could impersonate your server. It’s a good idea tomake the file readable only by the user that you use to run jabberd.

Next, you have to configure the server to open its client sockets for SSL connections.This requires editing the jabber.xml file. Find the <io> section and add a section likethis:

<ssl>

<key ip=’192.168.1.1’>/usr/local/jabber-1.4.2/key.pem</key>

</ssl>

Substitute the IP address of your service for 192.168.1.1 and the actual path to yourcertificate file for /usr/local/jabber-1.4.2/key.pem.Then configure the c2s serviceto use SSL on one or more client connection ports. Find the section within the <service id-”c2s”> element and add a line like this:

<ssl port=’5223’>192.168.1.1</ssl>

The IP address should match the SSL IP address in the <io> section.The port is arbi-trary, but can’t be the same as any other service on the computer (including the non-SSLc2s ports). Port 5223 is common for Jabber SSL connections. It would be nice to be ableto disable non-SSL connections, but that’s not possible with the 1.4.2 c2s component. Ifyou delete the <ip> tags, c2s will open the default port (5222) for unencrypted com-munications.You can open the default port and listen for only local connections (that is,connections from clients on the same host as the server) by including this line in the c2sconfiguration:

<ip port=”5222”>127.0.0.1</ip>

This will prevent random people from all over the Internet from connecting to yourserver.Alternatively, the great thing about open source software such as jabberd is that

08 0672325365 CH06 1/21/05 2:24 PM Page 262

Page 280: Jabber Developer’s Handbook [Sams 2004]

263Using SSL for Client Connections

you can modify the c2s component itself to disable opening unsecured ports and thenrecompile jabberd.

Save jabber.xml and restart the server. If you used the same port we did in the<ssl> tag shown earlier, it should open port 5223 for SSL connections.You can use thenetstat command to verify this:

$ netstat -an|grep 522

tcp 0 0 127.0.0.1:5222 0.0.0.0:* LISTEN

tcp 0 0 192.168.1.1:5223 0.0.0.0:* LISTEN

This shows the server listening for connections on port 5222 (the IP address127.0.0.1 means that only local connections will be accepted) and port 5223 (on thenetwork interface 192.168.1.1). Clients should be able to connect to the SSL port, carryout the SSL handshake, and send messages as normal.

Let’s try it out with the WinJab instant messaging client. In the connection prefer-ences, select the SSL port (5223, in this case) and select Use SSL Connection.Then con-nect as usual and the connection between the client and server is encrypted.

Figure 6.4 Configuring WinJab for SSL.

What’s involved in changing clients to connect with SSL? It varies depending on thelanguage’s support for SSL. In Java using JabberBeans, it’s straightforward.When makingthe original connection, replace the ConnectionBean with a ConnectionBeanSSL.Youjust need to change code like this:

ConnectionBean cb = new ConnectionBean();

cb.connect(InetAddress.getByName(server_host), 5222);

to this:

ConnectionBean cb = new ConnectionBeanSSL();

cb.connect(InetAddress.getByName(server_host), 5223);

08 0672325365 CH06 1/21/05 2:24 PM Page 263

Page 281: Jabber Developer’s Handbook [Sams 2004]

264 Jabber Security

That’s all the code changes. Now we need to add the certificate that we made for theserver to the list of certificates trusted by the Java runtime environment. Unlike WinJab,the Java runtime won’t accept a certificate unless it’s signed by a trusted authority.TheJava runtime includes several trusted certificates, but unless we get our certificate signedby one of them, we’ll have to manually add it to the list.

Recall that the openssl tool created the certificate and private key, which we put together in the file key.pem.The certificate in there is delimited by ——-BEGIN CERTIFICATE——- and ——-END CERTIFICATE——-. Using a text editor, extract that sectionfrom key.pem into another file, called cert.pem. It should look something like this:

——-BEGIN CERTIFICATE——-

MIIC1jCCAj+gAwIBAgIBADANBgkqhkiG9w0BAQQFADBXMQswCQYDVQQGEwJVUzER

MA8GA1UECBMIVmlyZ2luaWExEjAQBgNVBAcTCUFybGluZ3RvbjENMAsGA1UEChME

U0FNUzESMBAGA1UEAxMJbXktamFiYmVyMB4XDTAyMTEyNzE2MjgxOVoXDTEyMTEy

NDE2MjgxOVowVzELMAkGA1UEBhMCVVMxETAPBgNVBAgTCFZpcmdpbmlhMRIwEAYD

VQQHEwlBcmxpbmd0b24xDTALBgNVBAoTBFNBTVMxEjAQBgNVBAMTCW15LWphYmJl

cjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAyYw0TpeCc0ouRqr31NjLRBKq

o35rHOLMY9cbySk0TsKK/SgHcH/twuZPf0HT98n2Zsgwndzqy6y72oKwuFKz3809

0kidOMIaCD4Zilfq27OJhJEjpjbI27xXnw4blaOJgaGc5C6/EkfEPGFct8LyPiaO

uw2RMDtN5l+0TCGXOLMCAwEAAaOBsTCBrjAdBgNVHQ4EFgQU66D/K5DI11NZu6SE

pByjVK5bSMYwfwYDVR0jBHgwdoAU66D/K5DI11NZu6SEpByjVK5bSMahW6RZMFcx

CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhWaXJnaW5pYTESMBAGA1UEBxMJQXJsaW5n

dG9uMQ0wCwYDVQQKEwRTQU1TMRIwEAYDVQQDEwlteS1qYWJiZXKCAQAwDAYDVR0T

BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQBPkLgptz3A8GDYSdPCqfyZV7j/j7k+

v1idOdyRN+kLHLEB9KNyMcAoa5HdGyoiCClMOzafrd3a6vnhYJpef0D3hogmcEaU

Gjj3qILY9TEpcsIFB/81fXPdZFUfRB2nczNGlNbAuqEzc4PA5DQuY7dvzcSQ9Ash

lzH7ul3NqNA5yg==

——-END CERTIFICATE——-

Use the Java keytool utility to convert this certificate into a format that the Java run-time can understand.This command creates a Java keystore file that can be used as theset of trusted certificates:

$ keytool -import -alias myjabber -trustcacerts -file cert.pem -keystore

➥ myjabber.keystore

Enter keystore password: 123456

Owner: CN=my-jabber, O=SAMS, L=Arlington, ST=Virginia, C=US

Issuer: CN=my-jabber, O=SAMS, L=Arlington, ST=Virginia, C=US

Serial number: 0

Valid from: Wed Nov 27 11:28:19 EST 2002 until: Sat Nov 24 11:28:19 EST 2012

Certificate fingerprints:

MD5: C6:8B:25:E6:A9:D6:B7:90:21:EE:3C:FF:93:A1:9D:B3

SHA1: 9D:8E:7B:2D:B0:8C:8F:E7:73:74:F5:12:09:AB:9A:A0:D9:77:33:52

Trust this certificate? [no]: yes

Certificate was added to keystore

$

08 0672325365 CH06 1/21/05 2:24 PM Page 264

Page 282: Jabber Developer’s Handbook [Sams 2004]

265Server-to-Server Connection Authentication (Dialback)

The keystore password is arbitrary and must be at least six characters long. Becausetrusting a new certificate could be dangerous if done unwisely, the keytool utility printsthe contents of the certificate so the user can make sure it looks correct.

The only thing left is to tell the Java runtime to use the new keystore as its source fortrusted certificates.A Java property accomplishes this.Add -Djava.net.ssl.trustStoreto the command line. Here’s an example using the authentication tester you saw earlier:

$ java -Djavax.net.ssl.trustStore=myjabber.keystore org.jabber.AuthPlain

➥ my-jabber user1 user1 Work

Successfully authenticated

SSL-encrypted client sessions make it much more difficult for a potential hacker tosniff passwords and other confidential information over the network. It’s important toremember that the data is still decrypted and vulnerable when it is at the client and atthe server—SSL protects the data only while it is in transit.

Services that connect using the <accept> mechanism that you saw earlier in thischapter connect using an unencrypted link.As of this writing, SSL is not supported forcomponent-to-server connections or for server-to-server (s2s) connections.

Server-to-Server Connection Authentication(Dialback)So far you have seen how clients authenticate themselves to servers and how servicesauthenticate themselves to servers.This section examines how servers authenticate them-selves to each other.

A server needs to talk to another server whenever a client needs to send a packet to aclient or service that is resident on another server. For example, if user1@server1 sendsa message to user2@server2, authentication needs to occur in three places, as shown inFigure 6.5.

User1 User2server1 server2

Are you really whoyou claim to be?

Are you really whoyou claim to be?

Are you really whoyou claim to be?

Figure 6.5 End-to-end authentication.

Servers authenticate clients by maintaining a database of authorized users.Although itwould be possible to also require servers to maintain a database of all other servers withwhich they might communicate, that could be a heavy burden in working withInternet-scale numbers of servers. Fortunately, there already is a database of Internethosts in the form of the Domain Name Service (DNS).The server-to-server (s2s) com-ponent uses DNS to validate hostnames to ensure that server connections are from thehost they claim.This procedure is called dialback.

08 0672325365 CH06 1/21/05 2:24 PM Page 265

Page 283: Jabber Developer’s Handbook [Sams 2004]

266 Jabber Security

The name dialback must come from an old mechanism used by dialup servers to makesure that only authorized users used them. If Alice wanted to start a session with thedialup server, she’d follow this procedure:

1. Alice calls the dialup service and says,“This is Alice, I’d like a session please.”

2. The dialup server hangs up on Alice.

3. The server looks up Alice’s phone number and calls her back. Because the serverknows Alice’s phone number, no one else can impersonate her. (Unless they useher phone!)

4. Alice answers the phone and says,“I’ve been expecting your call.”

5. The session is established.

s2s uses the same principle, but uses DNS as the phone book. Figure 6.6 illustrates theprocess.

Figure 6.6 Dialback.

OriginatingServer

ReceivingServer

DNS AuthoritativeServer

Establish Connection

Send Stream Header

Send Stream Header

Send Dialback Key

Report Dialback Result

Exchange Messages(if Successful)

Lookup IP Address ofOriginating Server

Authoritative Server IP Address

Send Stream Header

Send Dialback Key

Verify Dialback Key

Send Stream Header

Establish Connection

08 0672325365 CH06 1/21/05 2:24 PM Page 266

Page 284: Jabber Developer’s Handbook [Sams 2004]

267Server-to-Server Connection Authentication (Dialback)

First, the originating server contacts the receiving server and establishes the connectionwith an exchange of stream headers.These headers use a specific XML namespace thatdistinguishes them from other connections.The stream header looks like this:

<stream:stream xmlns:stream=’http://etherx.jabber.org/streams’

xmlns=’jabber:server’ to=’ReceivingServer’ from=’OriginatingServer’

xmlns:db=’jabber:server:dialback’>

The receiving server responds with its own stream header:

<stream:stream xmlns:stream=’http://etherx.jabber.org/streams’

xmlns=’jabber:server’ from=’ReceivingServer’ to=’OriginatingServer’

xmlns:db=’jabber:server:dialback’ id=’some-random-string’>

The originating server then sends the dialback key to the receiving server.This is thekey that the receiving server will use to verify the identity of the originating server.

<db:result to=’ReceivingServer’ from=’OriginatingServer’>

the dialback key

</db:result>

Now the receiving server looks up the IP address for OriginatingServer in DNSand opens a connection to that server.This is probably, but not necessarily, the sameserver as the one with which it already has a connection. It sends a stream header likethis:

<stream:stream xmlns:stream=’http://etherx.jabber.org/streams’

xmlns=’jabber:server’ from=’ReceivingServer’ to=’OriginatingServer’

xmlns:db=’jabber:server:dialback’>

The authoritative server replies with its own stream header:

<stream:stream xmlns:stream=’http://etherx.jabber.org/streams’

xmlns=’jabber:server’ to=’ReceivingServer’ from=’OriginatingServer’

xmlns:db=’jabber:server:dialback’ id=’another-random-string’>

The receiving server can then ask the authoritative server to verify the dialback keywith a packet like this:

<db:verify from=’ReceivingServer’ to=’OriginatingServer’

id=’some-random-string’>the dialback key</db:verify>

The id attribute matches the ID of the stream header that the receiver sent the origi-nator when it first connected.The dialback key is the key that the originator provided.The authoritative server verifies the information (by whatever means it chooses) and, ifthe credentials match, replies to the receiving server with a packet with type valid andreplies to the original ID like this:

<db:result from=’OriginatingServer’ to=’ReceivingServer’

type=’valid’ id=’some-random-string’/>

08 0672325365 CH06 1/21/05 2:24 PM Page 267

Page 285: Jabber Developer’s Handbook [Sams 2004]

268 Jabber Security

If the credentials don’t match, it responds like this:

<db:result from=’OriginatingServer’ to=’ReceivingServer’

type=’invalid’ id=’some-random-string’/>

If all goes well and the credentials match, the receiving server can inform the origi-nating server with a packet like this:

<db:result to=’OriginatingServer’ from=’ReceivingServer’ type=’valid’ />

and packets can flow across the connection. Fortunately, all of this is handled transparent-ly by the s2s and dnsrv services.

The newly-authenticated connection is left open for a while so that if more messagesneed to flow between the two servers, the dialback doesn’t have to be repeated.Thelength of time that the connection is held open is controlled in jabber.xml by the s2sservice configuration parameter <idletimeout>, which defaults to 900 seconds (15 min-utes).This example section of the s2s configuration doubles that value to 30 minutes:

<service id=”s2s”>

<load>

<dialback>./libs/dialback.dll</dialback>

</load>

<dialback xmlns=’jabber:config:dialback’>

<idletimeout>1800</idletimeout>

<ip port=”5269”/>

Although using the dialback protocol is a clever way of authenticating servers, it hassome holes. First, it’s not that difficult to spoof DNS and hijack a hostname. Second, justbecause a host has an entry in DNS doesn’t mean that my server should accept messagesfrom it. Finally, there is no provision in the 1.4.2 s2s component for SSL connections, sodata flowing between servers is unencrypted.

SummaryYou’ve seen in this chapter that the security of the communications channels between partsof a Jabber system depends on several mechanisms. Figure 6.7 summarizes the Jabber secu-rity features covered in this chapter.

The communications channels in Figure 6.7 fall into three categories as follows:n Client-to-server (c2s).Authentication: JSM modules mod_auth_plain,mod_auth_digest, and mod_auth_0k. Confidentiality and Integrity: SSL.

n Server-to-service (accept and connect).Authentication: Shared secret hashedwith session ID. Confidentiality and Integrity: none.

n Server-to-server (s2s).Authentication: Dialback based on DNS entry.Confidentiality and Integrity: none.

08 0672325365 CH06 1/21/05 2:24 PM Page 268

Page 286: Jabber Developer’s Handbook [Sams 2004]

269Server-to-Server Connection Authentication (Dialback)

Figure 6.7 Jabber Communications Channels.

Although we expect that many of these shortcomings will be addressed in future ver-sions of jabberd, implementers of Jabber-based systems are free to use other securitymeasures, such as firewalls and Ipsec, to ensure the appropriate level of security for theirenvironments.

������ ������������ ������

������

�������

������

�������

08 0672325365 CH06 1/21/05 2:24 PM Page 269

Page 287: Jabber Developer’s Handbook [Sams 2004]

08 0672325365 CH06 1/21/05 2:24 PM Page 270

Page 288: Jabber Developer’s Handbook [Sams 2004]

II Jabber-Based Networked

Applications

7 What’s in a Name:Web Services

8 Jabber and Conversational Software Agents

9 Jabber and System Control and Administration

10 Jabber and JXTA

11 Jabber Libraries for Popular Languages

09 0672325365 Pt 2 1/21/05 2:25 PM Page 271

Page 289: Jabber Developer’s Handbook [Sams 2004]

09 0672325365 Pt 2 1/21/05 2:25 PM Page 272

Page 290: Jabber Developer’s Handbook [Sams 2004]

7What’s in a Name:Web Services

What’s in a name? that which we call a rose / By any other name would smell as sweet.

Romeo and Juliet,Act II, Scene 2.

WHEN IT COMES TO THE TOPIC OF WEB services, the Bard was right. If we practicereductionist logic, many people are attempting to gain mind- and marketshare by puttingtheir own spin on the concept of distributing logic over a network. Of course, each pur-veyor of middleware suites - some free and open source, some very expensive and closedor proprietary- claims to add some special value. Some add to their approach (servicediscovery methods, service naming schemes, language interoperability, transactional quali-ty of service, and so on), and in some cases these value-adds are actually useful, or at leastdeliver something close to the value of their hype.

Interestingly enough, we have had Web services in some form since shortly aftercomputing stepped out of its “primordial ooze” phase—since at least the advent of rela-tional database servers and client applications.We just didn’t have quite the flexibility ofdeployment and applications that we do now.What’s changed, of course, is the themethat’s at the very heart of this book—we now have XML messaging running betweenparts of an application.

First-Generation Applications: Servers and GlassTerminalsInitially, as depicted in Figure 7.1, the locus of the application was central and generallynot available beyond the hardwired connection between a timesharing terminal and amainframe.The essential character of applications in this era was data entry within theorganization against a mainframe-resident data base. Until the development of theARPANET and later the Internet, external access was not possible and the language ofthe conversation from the hardwired terminal and the mainframe was, by and large,

10 0672325365 CH07 1/21/05 2:25 PM Page 273

Page 291: Jabber Developer’s Handbook [Sams 2004]

274 What’s in a Name: Web Services

structured query language (SQL), and the response from the mainframe was a resultset ora status message.The semantics of the conversation were fairly fixed, as was the syntax,and the transport mechanism was bisynchronous and byte-oriented.The common gearof the day for a client was a 3270 glass terminal. Ironically, the emergence of the WorldWide Web and browsers would turn all those expensive PCs we bought in the 1990sback into glass terminals again, as you’ll see a couple sections from now.

Figure 7.1 3270 terminals and mainframes.

Second-Generation Applications: Servers andClientsSomewhat later the “hard wired” constraint was relaxed owing to the development ofnetwork protocols such as TCP (Transmission Control Protocol) and IP (InternetProtocol).The first distributed business-style client-server applications were built bysplitting the application locus into two parts.Thus, a client application, by this time running on a PC, could now be remote from the server application (Figure 7.2.) andtransmit and receive asynchronously. In the parlance of the ”Model-View-Controller”paradigm, the database server held the model, the client was responsible for generatingthe view, and both shared implementation of the controller.

������ �������������

�� ����

Figure 7.2 Initial networking of applications.

A variation of basic client-server design was the introduction of the so-called ”three-tierarchitecture,” which placed an application server next to the database server. Now thelocus of the application moved so that the client held only the view of the data.The appli-cation server held the controller logic, and the database server, the data model (Figure 7.3).

������ ����������� �� ��

���������������

10 0672325365 CH07 1/21/05 2:25 PM Page 274

Page 292: Jabber Developer’s Handbook [Sams 2004]

275Third Generation Applications: Enter the Web

Figure 7.3 AppServer plus DBServer.

The three-tier architecture started out fairly database-centric, but during the 1990s grewto encompass many other types of applications. On the plus side, an organization couldbuy “heavy iron” and improve the query time to a back-end server (such as a databaseserver), could host more sophisticated applications than the client PC might be capableof doing, and could manage with fairly slow (9600 BAUD) modem connections. On thedown side, the client-resident view component still needed custom development (usuallyin a nonportable GUI toolkit, such as the MFC, or Microsoft Foundation Classes).Thisbound applications to specific platforms and denied the benefits of the application fromother platforms.

Third Generation Applications: Enter the WebWith the mid-1990s emergence of the World Wide Web, applications could now takeadvantage of a uniform and ubiquitously deployed operating system feature (the Webbrowser) as a generic container for the view portion of a distributed application. In addi-tion to giving the Internet a de facto look and feel, the Web influenced the operationalcharacteristics of almost all distributed applications (see Figure 7.4).

������ ������� ���� ��

��������

������ ��

������ �� ������

�������

����������������

Figure 7.4 The Internet generation.

10 0672325365 CH07 1/21/05 2:25 PM Page 275

Page 293: Jabber Developer’s Handbook [Sams 2004]

276 What’s in a Name: Web Services

The Internet now emerged as a viable, secure, transaction-safe network for businesscommunications.Although early Web architectures allowed Web browsers to access data-bases directly, generally through the Common Gateway Interface (CGI) via HTTP, thereceiving end of the HTTP request was (and in some cases still is) a PERL program thatpicked apart the string data being passed in the body of the HTTP request.The struc-ture of the data is generally defined by the requirements of the servlet or similar programon the “server side” of the gateway.

Although this removed some of the complexity from the client side of the equationby making every client user experience similar to that of using a 3270 terminal, it alsoremoved some capabilities from the mix, notably the client’s ability to maintain statefulinformation, which is information about the ongoing session between the parts of theapplication.Additionally, it forced all applications to look pretty much the same, and alluser experiences to resemble filling in paper forms.

The possible upside was that at least the form you submitted was processed prettymuch instantly.Web application servers brought increased performance and scalability, aswell as enabling far more sophisticated business logic.

The most important difference between the newly emerged Web model and the ear-lier client-server paradigm was that applications no longer needed dedicated connectionsbetween the parts of the applications.Web applications were designed to communicate“through the network cloud,” that is, over the Internet—the ubiquitous public networkbased on simple agreed-upon standards like Hypertext markup language (HTML) andhypertext transfer protocol over IP (HTTP).

Ironically, after the emergence of the Web, two rather interesting things happened.First, connections between clients and servers no longer needed the participation of thetelephone companies to provide banks of dedicated phones at the server point of pres-ence. So, the overall valuation of the telecommunications industry dove to lows ininverse proportion to the valuation of any company that stuck a Web server in front ofits service (whether the service actually had value or not). Secondly, even clients locatedon the same network as a server (for example, in a corporate intranet) now tended touse HTTP for transport, a browser client, and a server.The model worked well for anydistributed application regardless of where the parts were located.

Really, though, the simple agreement over the carrier protocol (HTTP) and how todisplay something in a browser (HTML) pretty much describes the value that the Weboffers.There’s no agreement on how to find or describe a service, no standards on howto invoke a service’s methods or what data gets returned.To resolve those kinds of issues,we need to look beyond the simple Web sever model.

Fourth-Generation Applications: XML and WebServicesWith the advent of the Web services epoch, a new set of acronyms (XML-RPC, SOAP,WSDL, UDDI) are a part of the vocabulary of Web services.Among the claims for Web

10 0672325365 CH07 1/21/05 2:25 PM Page 276

Page 294: Jabber Developer’s Handbook [Sams 2004]

277Fourth-Generation Applications: XML and Web Services

services is that they are great enablers for business-to-business (B2B) automated interac-tions. Proponents suggest that they enable businesses to

n Improve collaboration with customers, partners, and suppliers by reducing integra-tion time and expense compared to custom-built B2B and P2P (person-to-person)solutions

n Increase revenue via expanded distribution channels, provide quicker time-to-market for new value-added services, and enable public discovery of existing assets

n Enhance customer service levels by allowing customers and trading partner accessto core systems

n Generate new revenue opportunities through creation of private trading networksor ecosystems

n Respond quickly to changing market conditions and customer preferences by uti-lizing loosely coupled, modular services

n Protect investments and avoid technology lock-in via standards-based architectureand hardware, operating system, and programming language neutrality

Surely if you’ve been plugging away at this book, at least a few of these arguments (thetechnical arguments, if not the business justifications) will sound strikingly familiar toyou, as they are the identical arguments for creating distributed systems with Jabber.

If we were to show the protocol stack for this emerging Web services architecture, itwould resemble Figure 7.5, although, as we will explain shortly, this is not quite accurate,and indeed as of the publication date of this book, large software vendors are busily try-ing to define, redefine, and create proprietary elements of this layer cake to suit their various goals of creating software dominance and lock-in.

Our biases and natural skepticism tend to make us favor “free and open” over “expen-sive and proprietary.”We therefore tend to lean toward concepts such as Jabber and awayfrom concepts that appear at first blush to be open, but may actually be battlegrounds forcapture and control of yet another piece of the infosphere.

Eventually, you may be called upon to render your technical justification for prefer-ring Jabber to creating “Web-service-like” architectures.At such times, you might wantto suggest that although your organization can certainly spend many thousands of dollarson middleware to facilitate a distributed service architecture, it can also choose toembrace Jabber, essentially for free.

That is not to say that there are not some free and open APIs at various levels of thestack shown in Figure 7.5; there are, as we will illustrate in code examples.The real ques-tion software artists should ask is,“Who controls the direction, destiny, implementation,and utility of the various layers of the protocol stack?” If it’s not the open source com-munity, then even the best APIs are simply decorations on a cake owned by someoneelse, a cake for which your organization may have to pay dearly for a slice.

10 0672325365 CH07 1/21/05 2:25 PM Page 277

Page 295: Jabber Developer’s Handbook [Sams 2004]

278 What’s in a Name: Web Services

Figure 7.5 Web service protocol stack.

Let’s look at how Web services are similar to and different from the service architecture,addressing scheme, and service discovery in Jabber. In Chapter 10,“Jabber and JXTA,”we will do a similar dissection of JXTA and Jabber. Later, we’ll show some examples ofinteroperating with these layers.

The most basic transport layers, HTTP over TCP/IP, need no introduction. If you arereading this book, we assume your mastery of them both conceptually and in writingcode.

XML-RPCXML-based Remote Procedure Call, or XML-RPC, is at the bottom of this rather talland heavy Web services protocol stack, directly above the Transport layer.An XML-RPCmessage is an HTTP-POST request in which the body of the request is encoded as XML. Whena procedure is executed on the remote peer, the value it returns is also formatted in XML.Does this sound familiar to you, the Jabber-savvy developer?

Procedure parameters can be scalars, numbers, strings, dates, or other primitiveobjects.Additionally, they can also be complex objects and list structures. Understandingthe data characteristics accompanying the call and return are the responsibility of thepeers involved in the conversation.This is very much like what happens in an interactionbetween Jabber clients, one or both of which may be systems and not humans. In thePython example shown here, an XML-RPC client is shown. Later, we’ll modify it toinclude Jabber capabilities.

Listing 7.1 reallySimpleXMLRPCInvoker.py

1:import xmlrpclib

2:aPeer = xmlrpclib.Server(‘http://localhost:8080’)

3:print xmlrpclib.dumps(tuple([“There was a young lady of Nantes”]),

‘aServer.Rot13’)

4:print aPeer.Rot13(“There was a young lady of Nantes”)

The output from running the client from the command line looks like Figure 7.6.

����

����

���

����� ��

������ �������� ��������

����� �����

10 0672325365 CH07 1/21/05 2:25 PM Page 278

Page 296: Jabber Developer’s Handbook [Sams 2004]

279Fourth-Generation Applications: XML and Web Services

Figure 7.6 Output from reallySimpleXMLRPCInvoker.

Just as a debug device lets you see what got transported over the wire, we printed online 4 the result of the call to dumps() whose Python signature isdumps(params,methodname=None, methodresponse=None, encoding=None). It con-verts an argument tuple (remember that in Python, a tuple is a read-only list) to anXML representation of the data needed to perform an XML-RPC request (or response,if the methodresponse option is used). It’s interesting simply as a learning device here,but will become slightly more useful when we mix XML-RPC with Jabber.

The transport mechanics of the protocol are simple, as illustrated by Figure 7.7. Oneside of the conversation acts as a simple HTTP server, fielding HTTP/POSTs from anycaller able to reach it by IP address (more about this shortly.)

Figure 7.7 P2P over XML-RPC.

��������

������

������

��� ����� ��

��� ���

���� ����

������ ������� ��� ���

������ ���� ��� ���������� ������� �� ���

10 0672325365 CH07 1/21/05 2:25 PM Page 279

Page 297: Jabber Developer’s Handbook [Sams 2004]

280 What’s in a Name: Web Services

The method call plus its consumed arguments are encoded in XML.The invoker estab-lishes an HTTP connection with the invokee.We are purposely choosing not to use thewords “client” and “server,” terms that sometimes conjure up levels of meaning that arenot intended. Here, we simply want you to think in terms of two peers conversing, usingHTTP as the bearer channel, and XML syntax to carry the semantics of the dialogue.

In this example, the invoker simply opens a channel on port 8080 at localhost, andthen invokes Rot13, hosted by a peer offering to respond on that URI, passing thatmethod a string value.The server’s Rot13 method shifts each alpha character in the inputstream by 13 letters (thus 1: And now for something completely differentbecomes 1: Naq abj sbe fbzrguvat pbzcyrgryl qvssrerag) and returns the con-volved string to the invoker.

Obviously, we’ve contrived the example such that both parties in the conversation arein agreement about where the server is (http://localhost:8080), the name of themethod on the server (Rot13), and what sort of input argument it consumes (a string). Ifwe hadn’t known all this a priori, the conversation between the parties would have failed.

Here’s the other side of the conversation, the XML-RPC service being invoked. It isequally simple.

Listing 7.2 reallySimpleXMLRPCInvokee.py

1:import SimpleXMLRPCServer

2:import xmlrpclib

3:class ReallySimpleXMLRPCService:

4: def Rot13(self, text):

5: rot= “”

6: for x in range(len(text)):

7: byte = ord(text[x])

8: cap = (byte & 32)

9: byte = (byte & (~cap))

10: if (byte >= ord(‘A’)) and (byte <= ord(‘Z’)):

11: byte = ((byte - ord(‘A’) + 13) % 26 + ord(‘A’))

12: byte = (byte | cap)

13: rot = rot + chr(byte)

14: return rot

15:

16:if __name__ == ‘__main__’:

17:

18: server = SimpleXMLRPCServer.SimpleXMLRPCServer((“localhost”, 8080))

19: server.register_instance(ReallySimpleXMLRPCService())

20: print ‘ReallySimpleXMLRPCService @ localhost:8080’

21: server.serve_forever()

We create a class defining a method offering the Rot13 algorithm (lines 3–15). On line18, we create a handle to a SimpleXMLRPCServer (standard Python library).

The output from running the server from the command line looks like Figure 7.8.

10 0672325365 CH07 1/21/05 2:25 PM Page 280

Page 298: Jabber Developer’s Handbook [Sams 2004]

281Fourth-Generation Applications: XML and Web Services

Figure 7.8 Output: Really simple XML-RPC server.

Notice that the output from the server invocation seems to suggest that an HTTP POSTwas made.Also, the additional slot /RPC2 was added to the request, making the actual callURI http://localhost:8080/RPC2.This is of course exactly right because the bearerchannel for the P2P XML-RPC request and response is HTTP.

Working with XML-RPC is roughly equivalent to creating a service for Jabber inmany respects. One side passes an XML stream and the other side interprets it.There aresignificant differences, though. One big difference is in the addressing scheme. In XML-RPC, the machine address and port of the called facility are fixed IP addresses staticallyassigned when a connection is made to the server.There is no explicit router acting as anintermediary.This might seem simpler in some cases, but it also limits deployment flexibili-ty.With Jabber, the addressing scheme used is an indirect reference (<service>@<server>/<resource>), which may map to any IP address you wish. If you need to move the serviceto a larger server, simply shut down the service and bring it back up on something beefier.You can redeploy a Jabber service ad lib with only small disruption to the service’s clients,and you can gracefully manage the service in terms of upgrade and replacement.

Over the wire, the client marshals the called method names and any parameters byencoding them in XML, as shown in Listing 7.3.

Listing 7.3 XML Encoding: Method Call

<?xml version=’1.0’?>

<methodCall>

<methodName>aServer.Rot13</methodName>

<params>

<param>

<value>

<string> There was a young lady of Nantes</string>

</value>

</param>

</params>

</methodCall>

10 0672325365 CH07 1/21/05 2:25 PM Page 281

Page 299: Jabber Developer’s Handbook [Sams 2004]

282 What’s in a Name: Web Services

The server does the same to return them to the caller, as shown in Listing 7.4.

Listing 7.4 XML Encoding: Method Response

<params>

<param>

<value><string>Gurer jnf n lbhat ynql bs Anagrf</string></value>

</param>

</params>

As you may have gathered from these snippets, the over-the-wire XML element tags arefairly simple.Table 7.1 illustrates.

Table 7.1 XML-RPC Over-the-Wire Packaging

XML Element Child Tags

<methodCall> A single <methodname> element

A single <params> element

<params> 1 or more <param> elements

<param> A single <value> element<value> A single simple or complex data type as defined in Table 7.2

A request may specify multiple parameters, defined in multiple <param> stanzas.Aresponse will have only a single <param> stanza.

XML-RPC has six defined data types and the mechanisms to create C-type datastructures from primitives.

Table 7.2 XML-RPC Data Types and Tags

XML TagXML-RPC Type Example Java Type Python Type

integer, 32 bits Either <i4> or <int>; int integerimplementation-dependantExample: <int>42</int>

string <string> String StringExample: <string>And Now

the News </string>

float, double precision <double> double doubleExample: <double>42.

200000000000003</double>

10 0672325365 CH07 1/21/05 2:25 PM Page 282

Page 300: Jabber Developer’s Handbook [Sams 2004]

283Fourth-Generation Applications: XML and Web Services

Boolean (0=false, 1=true) <int> or <boolean> (see note) Boolean int (after 2.3,Example: <int>1</int> Pythonor <boolean>1</boolean> bool type)(Python), <boolean>True</boolean> (Java)

date (format= <dateTime.iso8601> (see note) double doubleYYYYMMDDTHH: Example: <dateTime.iso8601>

MM:SS) 20030212T22:43:06

</dateTime.iso8601>

base 64 encoded binary <base64> UTF-7 UTF-7character data character data

NOTEData typing is critical for maintaining order in argument passing. Without respect to the implementation lan-

guage for the remote procedure and the local invoking process, the internal representations must be translat-

ed into something that can be XML-tagged and transported. The representation must be used for input and

output to XML-RPC systems if they require such types. Simply, if a remote procedure consumes a

Date/Time argument, you cannot send it a string such as 2003020813T23:12:31. Fortunately, XML-

RPC libraries for most popular languages have convenience routines that do the appropriate thing. In Python’s

xmlrpc library, you would construct an instance of the DateTime class, like so: datetime_arg =

xmlrpclib.DateTime(“2003020813T23:12:31”). Similarly, for a Boolean where the language

does not have a Boolean type (as in Python before 2.3): b = xmlrpclib.Boolean(True). This will be

true even when we look at blending XML-RPC with Jabber in a bit.

If any of this request and response dialog has a vaguely familiar look to you, perhaps it’sbecause it somewhat resembles a Jabber dialog.As you might conclude from the brevityand simplicity of the code samples, XML-RPC is easy to understand and employ. Ridingatop HTTP, it has at least one fatal flaw:You can’t use an XML-RPC service if you can’troute to its IP address (for example, behind a firewall).There is an appealing way aroundthis, and not surprisingly it involves using Jabber to replace HTTP as a transport, andusing Jabber’s alternate address space and Jabber Server as a routing mechanism.We willshow this shortly.

SOAPTo complete this tour of Jabber and Web Services, it’s important to talk about the rest ofthe protocol stack (Figure 7.5) and how Jabber might augment and enhance it.

Table 7.2 Continued

XML TagXML-RPC Type Example Java Type Python Type

10 0672325365 CH07 1/21/05 2:25 PM Page 283

Page 301: Jabber Developer’s Handbook [Sams 2004]

284 What’s in a Name: Web Services

Simple Object Access Protocol (SOAP) is intended to be (as the name implies) “sim-ple.” It’s a lightweight protocol intended for exchanging structured information in a decentralized, distributed environment. SOAP uses XML to create an extensible messagingframework.Again, this sounds a bit like Jabber, doesn’t it?

SOAP is very similar to XML-RPC. It works by marshaling procedure calls overHTTP as XML documents. SOAP was originally created as a collaboration amongMicrosoft and others.The initial public release was XML-RPC, enhanced with XMLnamespaces and customizable element names. Since then, however, SOAP has beenturned over a W3C working group.

The working group has been adding an interesting non-core set of features to SOAP.SOAP currently supports XML Schemas, enumerations, strange hybrids of structs andarrays, and custom types.At the same time, several aspects of SOAP are left open to theimplementer.This can open the door to incompatible implementations that benefit noone except large software vendors with their own agendas.

As you have seen in the XML-RPC examples, the Jabber <iq> element has beenextended to include an additional namespace for transporting and wrapping remote pro-cedure calls (called jabber:iq:rpc). No such extension currently exists, but one canimagine wrapping SOAP requests and enveloping them in a namespace (for example,jabber:iq:soap), and, in fact, there is a Jabber Extension Proposal (JEP) in this areaalready (see JEP-0072 at http://www.jabber.org/jeps/jeplist.php).

One approach you might consider (putting on your “architect” hat for a moment) isthat the Jabber <message/> element is extensible and could include arbitrary externalnamespace references and therefore could be used for carrying SOAP envelopes.Thiscould conceivably present some small potential for interoperability problems.Whatwould all the existing clients out there do if they received a SOAP request in a <message/>? The correct answer to this is that, as we have said previously, if applicationsdon’t understand or create handlers for subelements in the <message/> stanza, theyshould simply ignore them.

In summary, although you could use this approach, you should seriously think aboutwhether it could potentially impose any difficulties on clients’ applications that you mayhave already written and deployed in your enterprise.

WSDLWeb Services Description Language, or WSDL, is a bit more interesting, with potentiallygreater impact, than SOAP.WSDL is an XML format for describing network services as aset of endpoints operating on messages containing either document-oriented or procedure-orientedinformation.The operations and messages are described abstractly in XML. Services canthen be bound through the lower layers for the protocol stack to a real protocol.Thecurrent reference bindings are for SOAP, HTTP GET/POST, and MIME. If you areinterested in a more in-depth discussion of extended XML messaging and its place inWeb services overall, you might want to take a look at http://www.w3.org/2000/03/29-XML-protocol-matrix.

10 0672325365 CH07 1/21/05 2:25 PM Page 284

Page 302: Jabber Developer’s Handbook [Sams 2004]

285Fourth-Generation Applications: XML and Web Services

There are not currently any generally accepted Jabber strategies to address Web serv-ice description, for example querying a service to get its WSDL facets. One could imag-ine that it could be done via InfoQuery extension, as we described in the section on“SOAP.”

Listing 7.5 One Possible Way of Transporting WSDL Requests and Responses

Request

<iq type=”get” to=”MRPraline” id=”WSDL-one”>

<query xmlns=”jabber:iq:wsdl” />

</iq>

Response

<iq type=”result” to=”...” id=”...”>

<query xmlns=”jabber:iq:wsdl”>

<!— wsdl document content here—>

</query>

</iq>

As of this writing, however, there is not a serious JEP to do such a thing.Again, you canthink in terms (for the time being) of using <message/> subelements.

UDDIUniversal Description, Discovery, and Integration (UDDI) really isn’t the top of the Webservices protocol stack (as Figure 7.5 suggests), but rather a service discovery system thatmight be used with WSDL, SOAP, and other implementations of remote procedure call-ing. UDDI provides a searchable set of services, offering equivalents to telephone whitepages and yellow pages, as well as more technical “green pages.” Originally conceived asa centralized repository for all the world’s services, it has largely become decentralizedand made proprietary as organizations create strategies to control the path to informa-tion discovery on the Internet.There are no JEPs under consideration currently to useUDDI for Jabber service location or conversely, to transport UDDI requests, say in<iq/> stanzas.

If it appears that we are “waffling” by not presenting code examples that woulddemonstrate potential workarounds either for the WSDL or UDDI, remember that bothare still in relatively early phases of development.We can’t accurately gauge their ultimatedirection; IBM and Microsoft both have patent claims on WSDL, and UDDI is managedby a much more closed consortium than the W3C. So although it’s certainly possible(maybe even useful in some cases) to build a Jabber system that tunnels WSDL andUDDI documents in Jabber packets, the whole point of Web services is interoperability,and without standards, interoperability is limited to one’s sphere of control.

10 0672325365 CH07 1/21/05 2:25 PM Page 285

Page 303: Jabber Developer’s Handbook [Sams 2004]

286 What’s in a Name: Web Services

Jabber and XML-RPCWe spent several pages discussing the evolution of distributed applications and thendescribing Web services in general, and now you are probably wondering how Jabbercan enable and augment Web services.We have described how the Web services transportmechanisms (XML-RPC and SOAP) make it possible for applications divided by a net-work link to work together, how UDDI makes it possible for networked participants todiscover one another’s existence, and how WSDL makes it possible for the participants toarrive at the semantics (that is, the arguments and their types) for conversation.

It may have occurred to you that all this Web services complexity has simpler ana-logues in Jabber, and if it has, indeed we concur with you.We have shown in precedingchapters how Jabber works as a distributed service architecture, extending XML toencode data, transporting application-to-application, person-to-application, and system-to-system conversations, even through firewalls. Jabber also has a service discovery mech-anism for components and other clients acting as services (“servants”).

Why then should Jabber architects and developers concern ourselves with working inconcert with other mechanisms, such as the Web services protocol stack (which is quick-ly becoming closed and proprietary), or JXTA (open source but still experimental andevolving)? The short and simple answer is,“Because they’re there,” and because they(Web services, at least) are receiving and will continue to receive significant corporatelargess from very well-funded (gigantic, even) software companies whose interests aredeeply rooted in assuring that they capture and control as much of the distributed serv-ices market as possible.

Although Jabber can do much of what is done in the Web services stack, Jabber as anopen sourced resource does not fit a proprietary economic model. It can’t be easilyclosed and monetized by a giant software company. Of course, Jabber can still do some-thing that they ordinarily can’t do, and that is to solve the firewall problem.

As Figure 7.9 depicts, an invoking peer is on one side of a firewall.The invokerwould like to use code such as shown in Listing 7.1 to make use of the Rot13 service.Now, however, imagine that the service is on the other side of a firewall.An HTTP-POSTto a non-routable IP address will not be able to get through the firewall; thus, the callwill fail.We will show code for a couple ways around this problem. In both cases, theinvoker will be a simple Jabber peer connecting through a Jabber switch on the openinternet. One version of the invokee (invoked peer) will show bundling the remotemethod capability directly into the peer. In the other, an improved version shows aJabber client acting as a service relay for the same HTTP-based service we show inListing 7.2, which remains unchanged but can now exist inside a firewall.

10 0672325365 CH07 1/21/05 2:25 PM Page 286

Page 304: Jabber Developer’s Handbook [Sams 2004]

287Jabber-based RPC

Figure 7.9 Jabber acting as a service proxy for nonroutable IP addresses.

Jabber-based RPCWhat we’re going to show you is based on the fact that Jabber messaging uses an alter-native (to straight IP) address space and routes via a server. Remember that the XML-RPC specification defines an encoding of RPC requests and responses—a method call,the arguments it consumes, and the result it returns.The specification also mandatesHTTP as a transport.A Jabber-RPC strategy would replace HTTP as the transport,which as we’ve said makes it possible to overcome firewall and NAT-related problemstraditionally associated with HTTP port traffic.There’s at least one implementation thatdoes this directly (in PERL, for example, the Jabber::RPC module does this), but we’regoing to show you how to piece together a simple one of your own.

Jabber RPC InvokerIn Listing 7.1, a Jabber client connects to a Jabber switch. For convenience of demonstra-tion, we are connecting through a localhost using the name peer-a.The peer-a applica-tion is designed to connect to a Jabber endpoint peer-b.What’s a little different from thenorm here is that there is no mutual roster exchange required.That is, peer-a and peer-b are not going to be rigged to exchange messages; rather what will happen is that thedata encoded for the RPC will be wrapped in an <iq> packet. Because the Jabber switchwill not prevent the flow of infoqueries, delivery is assured to the other side.

ON

Off

FIREWALL

JABBERSwitch

ServerProxy

invoker invoked

JABBER HTTP

HTTP

10 0672325365 CH07 1/21/05 2:25 PM Page 287

Page 305: Jabber Developer’s Handbook [Sams 2004]

288 What’s in a Name: Web Services

Listing 7.6 JabberRPCPeerA.py: Invoking

1:import jabber

2:import xmlrpclib

3:import string

4:import sys

5:

6:Server = ‘localhost’

7:Username = ‘peer-a’

8:Password = ‘peer-a’

9:Resource = ‘rpc’

10: Endpoint = ‘peer-b@localhost/rpc’

11: Method = ‘Rot13’

12:

13:text = “There was a young lady of Nantes”

14:request = xmlrpclib.dumps(((text),), methodname=Method)

15:#print request

16:con = jabber.Client(host=Server)

17:try:

18: con.connect()

19:except IOError, e:

20: print “Unable to connect: %s” % e

21: sys.exit(0)

22:

23:con.auth(Username, Password, Resource)

24:

25:iq = jabber.Iq(to=Endpoint, type=’set’)

26:iq.setQuery(‘jabber:iq:rpc’)

27:iq.setQueryPayload(request)

28:

29:result = con.SendAndWaitForResponse(iq)

30:#print “Done! Result:”, result

31:

32:if result.getType() == ‘result’:

33: response = str(result.getQueryPayload())

34: parms, func = xmlrpclib.loads(response)

35: print parms[0]

36:else:

37: print “Error”

38:

39:con.disconnect()

The calling parameters are set up on lines 6–12. Note the import of xmlrpclib on line2, which has methods for bundling and unbundling parametric data as well as handlingthe actual XML-RPC protocol itself. Line 14:

14:request = xmlrpclib.dumps(((text),), methodname=Method)

10 0672325365 CH07 1/21/05 2:25 PM Page 288

Page 306: Jabber Developer’s Handbook [Sams 2004]

289Jabber-based RPC

uses the dumps method to convert the argument that (in this Python library) must beconverted from a Python tuple to a properly encoded XML-RPC request (or response,if the methodresponse optional argument is used). If you want to see the XML createdin the request, uncomment line 15.The encoding was shown previously in Listing 7.3.

Lines 16–23 log the user into the switch (substitute any valid user). Next, the criticalpart: Create a user peer-b on your Jabber switch. For simplicity we are using localhost.Assign the resource rpc to the username.

Create an infoquery packet (line 25) addressed to peer-b@localhost/rpc. Next, setthe query itself to a remote procedure call, then set the payload to the XML stanza youcreated (lines 26–27).

25:iq = jabber.Iq(to=Endpoint, type=’set’)

26:iq.setQuery(‘jabber:iq:rpc’)

27:iq.setQueryPayload(request)

28:

29:result = con.SendAndWaitForResponse(iq)

Finally, make the call and wait synchronously for the response.The returned responseobject contains the return type and an encoded payload.

We test the result type to determine whether we got a good response or a “fault”(that is, an error), get the payload, and then load the returned stanza (shown earlier inListing 7.3) into a Python structure using loads() (lines 32–35).The parms structure isan array, but normally only the zero-th slot is filled.

32:if result.getType() == ‘result’:

33: response = str(result.getQueryPayload())

34: parms, func = xmlrpclib.loads(response)

35: print parms[0]

Jabber RPC Service Provider (First Version)Listing 7.7 looks at code that will respond to the invoker we just wrote. It bundles theinvoked function into its code body.

Listing 7.7 JabberRPCPeerB.py: Bundling the Remote Method with a Jabber Peer

1:import xmlrpclib

2:import jabber

3:import sys

4:import string

5:import sys, re, os, string

9:Server = ‘localhost’

10:Username = ‘peer-b’

11:Password = ‘peer-b’

10 0672325365 CH07 1/21/05 2:25 PM Page 289

Page 307: Jabber Developer’s Handbook [Sams 2004]

290 What’s in a Name: Web Services

12:Resource = ‘rpc’

16:# Global procedures:

17:def Rot13(text):

18: rot= “”

19: for x in range(len(text)):

20: byte = ord(text[x])

21: cap = (byte & 32)

22: byte = (byte & (~cap))

23: if (byte >= ord(‘A’)) and (byte <= ord(‘Z’)):

24: byte = ((byte - ord(‘A’) + 13) % 26 + ord(‘A’))

25: byte = (byte | cap)

26: rot = rot + chr(byte)

27: return rot

31:def iqCB(con, iq):

37: query_ns = iq.getQuery()

38:

39: payload = iq.getQueryPayload()

40: #print “Payload—>\n”, payload

41: payload = xmlrpclib.loads(str(payload))

42: #print “Payload.loads()—>\n”, payload

45: if query_ns == jabber.NS_RPC:

46: resultIq = jabber.Iq(to=iq.getFrom(), type=’result’)

47: resultIq.setID(iq.getID())

48: resultIq.setFrom(iq.getTo())

49: query_result = resultIq.setQuery(iq.getQuery())

50: resultIq.setQuery(jabber.NS_RPC)

51: evalString = payload[1]+”(‘“+payload[0][0]+”’)”

52: returnParams = xmlrpclib.dumps(tuple([eval(evalString)]))

53: print “returnParams==>\n”, returnParams

54: resultIq.setQueryPayload(returnParams)

55: con.send(resultIq)

56:

57:

58:def presenceCB(con, pres):

59: “Got PresenceCB”

60: pass

61:

Listing 7.7 Continued

10 0672325365 CH07 1/21/05 2:25 PM Page 290

Page 308: Jabber Developer’s Handbook [Sams 2004]

291Jabber-based RPC

62:def messageCB(con, msg):

63: “Got messageCB”

64: pass

65:

66:#————- “MAIN” ——————————————-

67:

68:

69:con = jabber.Client(host=Server)

70:

71:try:

72: con.connect()

73:except IOError, e:

74: print “Couldn’t connect: %s” % e

75: sys.exit(0)

76:else:

77: print “Connected”

78:

79:con.process(1)

80:

81:if con.auth(Username, Password, Resource):

82: print “Authorised”

83:else:

84: print “problems with handshake: “, con.lastErr, con.lastErrCode

85: sys.exit(1)

86:

87:con.setMessageHandler(messageCB)

88:con.setPresenceHandler(presenceCB)

89:con.setIqHandler(iqCB)

90:WAITSECONDS = 300

91: try:

92: while(1):

93: con.process(WAITSECONDS)

94: except KeyboardInterrupt:

95: con.disconnect()

96:

First, we set up an identity for the service.This can be anything—just make certain it’sthe same target as line 10, Listing 7.6. Lines 17–27 are the Rot13 method shown in

Listing 7.7 Continued

10 0672325365 CH07 1/21/05 2:25 PM Page 291

Page 309: Jabber Developer’s Handbook [Sams 2004]

292 What’s in a Name: Web Services

Listing 7.2. Neither the presence or message handler callbacks (lines 58–64) are particu-larly interesting; they are not involved in the scenario.We extract the query’s namespaceon line 37 and get the payload on line 39. Uncomment the print lines if you want to seewhat the payload looks like as encoded XML and then as a Python structure.

We check the query type and if it’s appropriate we start formulating a reply. Much ofthis kind of response formation you’ve already seen. Notice that we set the type toresult, which is what the invoker expects. On line 51, we construct a string from theunpacked XML and use the string in an eval to invoke the Rot13 method (dynamiclanguages are fun, aren’t they!). On line 52, we get a result from the method, create aPython tuple from it, and use dumps() to form up the return XML. Uncomment line53 if you want to examine them. Finally, we set the payload and return it to the invoker.

The driver for the process begins on line 69, where we create a connection object,handshake with the switch, and set the handlers and process in a periodic loop, inter-ruptible by a Ctrl+C.

To try this out, simply open a pair of command windows and run each application.

NOTEYou might be wondering why we created a client rather than a component in the previous example. In fact,

you could have constructed the service provider as either a component or a client. The difference for this

exercise is immaterial. Just remember that components take a bit more setup in the configuration of your

jabber.xml. For ease of demonstration, a client is rather easier.

Jabber RPC Service Provider (Second Version)Looking back at Figure 7.9, you will notice that we completed only the first leg of thejourney.We rolled the service from Listing 7.2 right into the Jabber endpoint.What if,instead, you wanted to keep the original responder as is, (that is, an XML-RPC servicewith an HTTP transport)? You might want to do this for a variety of reasons:

n Tunneling one or multiple firewalls. (Figure 7.9 shows only a single firewall, butthere’s no reason why both Jabber clients might not be behind firewalls.) Thisbecomes a very difficult situation in the HTTP world, as you may be aware. (Thisis the most obvious reason for refactoring the application in this manner, but alsoconsider the other motivations, as well.)

n Keeping a single code base for the remote method itself (instead of having to sup-port it in two places, the Jabber-RPC version and the original XML-RPC ver-sion).

n Maintaining the original interface for Web-based users.n Providing multiple access methods.

The easiest way to accomplish separation of functional concerns is to keep the serviceprovider just as it was in Listing 7.2 and put a Jabber client inside the firewall with it.

10 0672325365 CH07 1/21/05 2:25 PM Page 292

Page 310: Jabber Developer’s Handbook [Sams 2004]

293Jabber-based RPC

The Jabber client acts as a service proxy for the service provider trapped inside the fire-wall, and although we don’t show this aspect, we can do user authentication on behalf ofthe service provider.

Listing 7.8 JabberRPCPeerC.py: Using Service Proxy

1:import xmlrpclib

2:import jabber

3:import sys, re, os, string

4:

5:Server = ‘localhost’

6:Username = ‘peer-b’

7:Password = ‘peer-b’

8:Resource = ‘rpc’

9:

10:ServiceEndpoint = “http://localhost:8080”

11:

12:# Global procedures:

13:

14:def iqCB(con, iq):

15: query_ns = iq.getQuery()

16:

17: if query_ns == jabber.NS_RPC:

18: payload = iq.getQueryPayload()

19: #print “Payload—>\n”, payload

20: payload = xmlrpclib.loads(str(payload))

21: #print “Payload.loads()—>\n”, payload

22: rotServer = xmlrpclib.ServerProxy(ServiceEndpoint)

23: payload = payload[0][0]

24: #print “payload==>”,str(payload)

25: rotText = rotServer.Rot13(payload)

26: resultIq = jabber.Iq(to=iq.getFrom(), type=’result’)

27: resultIq.setID(iq.getID())

28: resultIq.setFrom(iq.getTo())

29: resultIq.setQuery(jabber.NS_RPC)

30:

31: rotText = xmlrpclib.dumps(tuple([rotText]))

32: resultIq.setQueryPayload(rotText)

33:

34: con.send(resultIq)

35:

36:

37:def presenceCB(con, pres):

38: pass

39:

40:def messageCB(con, msg):

41: pass

10 0672325365 CH07 1/21/05 2:25 PM Page 293

Page 311: Jabber Developer’s Handbook [Sams 2004]

294 What’s in a Name: Web Services

42:

43:#————- “MAIN” ——————————————-

44:

45:con = jabber.Client(host=Server)

46:

47:try:

48: con.connect()

49:except IOError, e:

50: print “Couldn’t connect: %s” % e

51: sys.exit(0)

52:else:

53: print “Connected”

54:

55:con.process(1)

56:

57:if con.auth(Username, Password, Resource):

58: print “Authorised”

59:else:

60: print “problems with handshake: “, con.lastErr, con.lastErrCode

61: sys.exit(1)

62:

63:con.setMessageHandler(messageCB)

64:con.setPresenceHandler(presenceCB)

65:con.setIqHandler(iqCB)

66:

67:WAITSECONDS = 300

68:try:

69: while(1):

70: con.process(WAITSECONDS)

71:

72:except KeyboardInterrupt:

73: con.disconnect()

74: os.exit(0)

Let’s look at a simple case in Listing 7.8.To test this code, you should start up thereallySimpleRPCServer from the command line so that there will be a running targetfor your client. In this client, just as before, we provide a very minimal callback for message types other than infoqueries (lines 37–41).The “main logic” (lines 45–73) isidentical to the old version.The big change is in the infoquery callback.

Again, we check for the namespace of the query, and if appropriate, unbundle it as wedid previously.

Listing 7.8 Continued

10 0672325365 CH07 1/21/05 2:25 PM Page 294

Page 312: Jabber Developer’s Handbook [Sams 2004]

295Jabber-based RPC

If you uncomment the print statement on line 19, you get the actual XML payload:

<methodCall>

<methodName>Rot13</methodName>

<params>

<param>

<value>

<string>There was a young lady of Nantes</string>

</value>

</param>

</params>

</methodCall>

This is not quite what we want to pass to the remote XML-RPC, though, at least notin the Python implementation used in the example. It actually wants to see the rawargument to the remote method, so we have to go through the pain of converting fromXML to an internal data structure.That’s why we do what you see on line 20:

20: payload = xmlrpclib.loads(str(payload))

If you uncomment the next print line, you will see the internal data structure, a list ofitems, the first of which is a list (the potential argument list for the remote method):

((‘There was a young lady of Nantes’,), u’Rot13’)

Of course, a proper implementation would do a lot more grooming of the payload,but because we know exactly what the remote method requires, we take only the firstmember of the first list slot (the string “There was…”).

After making the method call (line 25), we set up the return packet and send it off.Try this out, and you should see the resulting Rot13’d string echoed on the command

line. If you run the server in debug mode and observe the traffic, you will notice thesame payload shown in Listing 7.3. However, because the payload is being transported viaJabber rather than HTTP, it’s wrapped in the outer Jabber envelope, shown in bold print:

Listing 7.9 XML-RPC Wrapped for Jabber Transport

<iq to=’peer-b@localhost/rpc’ type=’set’ id=’1’>

<query xmlns = ‘jabber:iq:rpc’ >

<methodCall>

<methodName>Rot13</methodName>

<params>

<param>

<value>

<string>There was a young lady of Nantes</string>

</value>

</param>

</params>

</methodCall>

</query>

</iq>

10 0672325365 CH07 1/21/05 2:25 PM Page 295

Page 313: Jabber Developer’s Handbook [Sams 2004]

296 What’s in a Name: Web Services

Jabber-RPC Object LessonsXML-RPC is implemented to target a domain or IP address, and reference implementa-tions of SOAP have this limitation as well.We showed a simple way around this bybridging XML-RPC over Jabber.

The object lesson here is that you can use Jabber to transport XML-RPC and SOAPmessages. Depending on the library implementation, you may have to do more or lesswork. Instead of using normal DNS or IP addressing, calls and results are routed betweenJabber entities by JID. Distributed applications, including access to a personal desktopbehind a firewall and groupware applications, can reach any Jabber-enabled desktop.People using Jabber as their projection in cyberspace can move around without losingconnectivity to their applications. It is noteworthy to mention that the Jabber SoftwareFoundation moved “JEP-0009:Transporting XML-RPC over Jabber” final status in late2002, making it the transport of choice for XML-RPC traffic over Jabber. (See http://www.jabber.org/jeps/jep-0009.html for details.)

NoteFor the ultra-curious out there, we can foresee a flood of emails asking for the completion of the limerick

used in our Rot13 example. There may be several versions, but the two that we admit to knowing are:

There was a young lady of NantesWho said to her boyfriend, “You can’t!”But he was a sticklerFor usage particularAnd said, “Yes I could, but I shan’t.”

And:

There was a young lady of Nantes, Who lived with a miserly aunt; When asked to a ball, Said: ‘I’ve no clothes at all. I must borrow the plumes of my tante.’

So now you know.

SummaryWhat we learned in this chapter is that there is an emerging (though hardly mature)stack of Web services that seek to structure distributed computing in a way that is mostcongenial to B2B users and to their own commercial agendas.We in the conversationalsoftware community should understand that in the short term, the evolution of lan-guages and systems is likely to produce all sorts of curiosities and dead ends.We canembrace and coexist with these turns just as Cro-Magnon may have coexisted with

10 0672325365 CH07 1/21/05 2:25 PM Page 296

Page 314: Jabber Developer’s Handbook [Sams 2004]

297Summary

Neanderthal, but that does not mean that we need to believe that certain developmentsin computing represent any sort of evolutionary end game. Just looking at the evolutionof architectures depicted in Figures 7.1–7.4 illustrates how many different approaches toachieving distribution there have been, none of which have proven to be a “final form.”Are Web services likely to be a main evolutionary branch or simply another iteration inthe evolution of “systems of systems?”We believe the answer is that they are the latter,and that conversational systems based on Jabber and its descendants are likely to be thewave of the future.

It’s our belief that future systems will indeed be based on a conversational, message-based transport architecture (like Jabber), because all network endpoints will look like loosely coupled, highly autonomous, distributed computational entities with conversation-like syntactic/semantic capabilities.We believe that in using the techniquesand philosophies of this book, you will be working with and adding to the body ofknowledge leading to the continued productive evolution of systems.

In the next chapter, we go a little far out into suggesting a current shaping for “con-versational systems” by applying a little artificial intelligence research that you can easilyincorporate into your Jabber-based applications.We think it’s one of the more fun diver-sions you will want to try, and to that end, let’s move on to Chapter 8 and the topic of“Jabber and Conversational Software Agents.”

10 0672325365 CH07 1/21/05 2:25 PM Page 297

Page 315: Jabber Developer’s Handbook [Sams 2004]

10 0672325365 CH07 1/21/05 2:25 PM Page 298

Page 316: Jabber Developer’s Handbook [Sams 2004]

8Jabber and Conversational

Software Agents

There is no such thing as conversation. It is an illusion.There are intersecting monologues, that is all.

Rebecca West

What a delightful thing is the conversation of specialists! One understands absolutely nothing and it’s charming.

Edgar Degas

Recreation, not instruction, is the aim of conversation.

Oscar Wilde

THIS NEXT PROJECT ENABLES HUMAN CLIENTS to talk to no one (literally), and whetherthe objective is a simple recreational application or a portal to specialized services, youwill be able to build a conversational agent that serves a number of simultaneous usersand sometimes returns intelligent answers to questions.Whether or not the answers thatyour applications return to users will appear sensible is largely a matter of whether youhave constructed an intelligent dialogue system in something called AIML (ArtificialIntelligence Markup Language), which we’ll describe in a bit.The easier part will beusing a Jabber backbone to create the application.We say “easier” rather than “easy”because two servers are involved with this application—your familiar Jabber server andsomething called an Alicebot server—which converse with users.There will be a bit of

11 0672325365 CH08 1/21/05 2:25 PM Page 299

Page 317: Jabber Developer’s Handbook [Sams 2004]

300 Chapter 8 Jabber and Conversational Software Agents

work building a conversational multiplexor into the Alicebot server, but stick with us.We’ll walk you through and have your smart conversational agent up and talking to usersin no time.

Motivating This ApplicationFirst of all, why would you want to build a conversational agent? There are many rea-sons, we think. Let’s say that you want to build a command interface to an application. Itmakes no difference whether the application is distributed or local, but your frameworkshould accommodate either.Working out the command structure and forcing the user toremember the arcane language of the command-line interface (as we did in the invento-ry update application in Chapter 5,“Extending the Jabber Server,” for example) is a pain;not to you as the application designer perhaps, but certainly to the user. It would be bet-ter if a user could talk to the command-based application in a way that was more like anatural conversation in a natural language.That’s one goal (of many) of AIML and theAlice open source project.

Further, because the model of instant messaging has accustomed us to deferredresponses from the entities (usually humans) on the other end of a conversation, applica-tions are transitioning from using simple tools, such as spreadsheets where the response tothe user must be relatively instantaneous, to using potentially complex services, where thesystem response can come at any time and alert the user when the response is ready.Weas developers should be getting acquainted, at least, with building this style of applica-tion.

Accomplishing the ObjectiveAs we’ve suggested in various spots in this book, Jabber applications may be componentsor they may be clients. In this chapter, we build a client-style application that acts as anintermediary to an external service.The application looks a bit weird, because the clientthat’s built is actually a multiplexor compiled into the Java implementation of theAlicebot server itself.

This will be a busy chapter, because we introduce a fairly complex and probablyunfamiliar technology. Ironically, as we already said, the easy part will be to create theJabber client portion.

What Is Alice?Alice is, simply put, a “chatterbot”—a software entity that can respond to your typed-inconversation.The degree to which chatterbots like Alice can hold intelligent conversa-tion is the topic of much discussion and debate, but for our purpose we will state that,given a sufficiently constrained topical area, that Alice seems intelligent enough.Alice

11 0672325365 CH08 1/21/05 2:25 PM Page 300

Page 318: Jabber Developer’s Handbook [Sams 2004]

301Alice Design Principles

supplies responses to your input using its own built-in parser and canned responses, or byexecuting bits of ECMAScript (JavaScript) to mine external resources.This latter capa-bility enables Alicebots to expand their repertoire of knowledgeable responses.Essentially, the Java versions (available at www.alicebot.org) of the Alice Server embedthe Rhino ECMAScript interpreter. Rhino enables you to invoke any Java class.ECMAScript enables you to interact by using HTTP or sockets to connect with exter-nal resources.

Why Alice Appears to WorkA conversation with an Alicebot can (depending on emergent properties of the dis-course) appear to reveal a fair amount of intelligence on the part of the virtual partici-pant (if not the human).According to Dr. Richard Wallace,Alice’s innovator, there is afundamental reason for this, which has to do with the nature of conversational interac-tion in a human language. He says,“Considering the vast size of the set of things peoplecould say that are grammatically correct or semantically meaningful, the number ofthings people actually do say is surprisingly small.”

In short, people don’t say a lot of original things or phrase them in unusual ways.Thisgives you as a developer a reasonable assurance that people using your AI system will tryto say sensible things, and that your code can yield believable responses.

Alice Design PrinciplesThe Alicebot server shares a few design principles with the Jabber server. For one thing,it gains its startup knowledge from an XML-based database called startup.xml (nor-mally found in <ALICE-installdir>/conf).Also, like Jabber, it allows plugin of addi-tional capability.We will use this to plug in our own custom Jabber transport so that anynumber of humans (or other applications) can query Alice.

Like Jabber,Alice has core components, and to add a new core component, you willhave to compile it into the server.Alice already has a built-in conversational interpreter, amessage callback mechanism, support for several kinds of GUI clients, and a multi-clientmultiplexor. If you haven’t run into the term multiplexor before, here’s how the idea isexplained by Dr. Richard Wallace,Alice’s inventor:

The Multiplexor controls a short “carnival ride” for each user.The Multiplexor putsthe client in his/her seat, hands him/her an ID card, and closes the door.The clientgets one “turn of the crank.” He/she enters his/her ID, multiline query, and thenreceives the reply.The door opens, the Multiplexor ushers him/her out, and seats thenext client.”[1]

1 To gain more insight into the entire Alice philosophy and effort, make sure to visit www.alicebots.org, but be warned you may be sucked into a whole new, deep, and potentiallymind-expanding realm.

11 0672325365 CH08 1/21/05 2:25 PM Page 301

Page 319: Jabber Developer’s Handbook [Sams 2004]

302 Chapter 8 Jabber and Conversational Software Agents

The Alicebot ServerTo use or extend Alicebot, you should download the server package from Alicebot.org.(See “Resources” in Appendix C.) Specifically for this chapter, you should downloadProgram D (the Java implementation) from http://www.alicebot.org/downloads/and the associated tools.When you download and install on Windows, your installationshould resemble Figure 8.1.

Figure 8.1 Alice’s installed files.

The following sections highlight some of the more interesting folders installed with theJava Alice Server.

AIML Folder—Alice’s Built-in KnowledgeYou should see an AIML folder that is the root folder for the standard knowledge baseshipped with Alice. Each of the snips of knowledge is contained in an .aiml file.AIMLfiles are simply an XML dialect understood by the Alice Server.You can extend theknowledge base by adding folders containing .aiml files; as long as these are mentionedin the appropriate stanzas in the startup.xml configuration file (see “AliceConfiguration Files,” later in this chapter), they will be added to the knowledge base.

TipTo learn AIML in deep detail, see Appendix C for resources. We will cover enough AIML in shallow detail

here so that you will be able to get a good start on defining some custom knowledge on your own.

11 0672325365 CH08 1/21/05 2:25 PM Page 302

Page 320: Jabber Developer’s Handbook [Sams 2004]

303The Alicebot Server

There is, for example, a file called dev-calendar.aiml, which responds to questions ofthe type “What’s the date?”“What’s today?” or “What time is it?”This particular file isinteresting because it exemplifies the use of ECMAScript and Rhino to invoke the JavaDate class.

Lib Folder—Alice ProgramD’s Java LibraryThe lib folder, shown in Figure 8.2, contains several jar files. Until you add the jabber-beans.jar, you won’t see that one.You may want to customize and build theAliceserver.jar (for example to incorporate your own version of the Jabber logic), soit may be sized differently.

Figure 8.2 Alice jar files.

The aliceserver.jar contains the basic Alice logic plus the custom Jabber library wewill build in this chapter.The Jabber multiplexor (mux) code, which we also build, isbundled into JabberLib.jar. Jabberbeans.jar is the standard Java Jabber capability.The js.jar holds the Rhino ECMAScript interpreter. Other libraries are mysql.jar(database services), servlet.jar (for HTML Alice clients), xerces.jar (XML parser),and org.mortbay.jetty.jar (embedded Web server capabilities).As we suggested earli-er,Alice plugs in a number of service capabilities by default and allows you to extend theavailable services.The configuration file startup.xml informs the Alice server of exten-sions.

Alice Configuration FilesLet’s take a closer look at startup.xml so that we can understand Alice generally andsee where the Jabber mux is to be plugged in to.

1:<?xml version=”1.0”?>

2:<!--This is an example startup.xml file for Program D.-->

3:<!--programd-startup is the root element and is required.-->

4:<programd-startup>

11 0672325365 CH08 1/21/05 2:25 PM Page 303

Page 321: Jabber Developer’s Handbook [Sams 2004]

304 Chapter 8 Jabber and Conversational Software Agents

The <bots> tag (lines 5 and 79) surround everything of significance in the file.Thefirst bot specified is called Alice, the second (specified after line 61) describes anotherchatterbot.You can change out the bot you’re talking with on the fly by sending themessage /talkto <bot-name>. Each chatterbot can have its own knowledge base; youcan therefore have bots knowledgeable in a variety of subject domains if you like.

5: <bots>

6: <bot id=”Alice” enabled=”true”>

7: <!--Bot predicates are set using the property tag. These are just ➥ examples.

8: Be sure to set properties BEFORE loading AIML files.-->

9: <properties>

10: <property name=”name” value=”Alice”/>

11: <property name=”master” value=”A.L.I.C.E. AI Foundation”/>

12: </properties>

The <listeners> tag pair (lines 15 and 43) define the muxes that will link Alice tovarious transports. Shown and defined are an IRC (Internet relay chat) mux (lines 22-27), an AOL Instant Messenger mux, and most importantly, a new mux for Jabber trans-port,AliceJabber (lines 22-27), which will be defined and created in this chapter (shownin bold).We define a host, userid, password, and resource.To use this mux to the Alice,you should create a client that corresponds to this definition, using a GUI client or oneof the scripts you saw in Chapter 1. For example, you can do:

python newUser.py localhost alice alice Alice

You can specify whatever parameter values you want in startup.xml, but these val-ues must correspond to the client you create.

13: <!--Listener types must correspond to the appropriate

14: identifiers for listeners that are available in

➥ your classpath.-->

15: <listeners>

16: <listener type=”AliceJabber” enabled=”true”>

17: <parameter name=”host” value=”localhost”/>

18: <parameter name=”userid” value=”alice”/>

19: <parameter name=”password” value=”alice”/>

20: <parameter name=”resource” value=”Alice”/>

21: </listener>

22: <listener type=”AliceIRC” enabled=”false”>

23: <parameter name=”host” value=”ar.chatjunkies.org”/>

24: <parameter name=”port” value=”6667”/>

25: <parameter name=”nick” value=”programd”/>

26: <parameter name=”channel” value=”#bots”/>

27: </listener>

34: <listener type=”AliceAIM” enabled=”false”>

35: <parameter name=”owner” value=”your-name”/>

11 0672325365 CH08 1/21/05 2:25 PM Page 304

Page 322: Jabber Developer’s Handbook [Sams 2004]

305The Alicebot Server

36: <parameter name=”screenname”

➥ value=”your-bot-screen-name”/>

37: <parameter name=”password” value=”your-bot-password”/>

38: <parameter name=”fontface” value=”Verdana,Arial”/>

39: <parameter name=”fontcolor” value=”Black”/>

40: <parameter name=”bgcolor” value=”White”/>

41: <parameter name=”buddies” value=”your-bot-buddies”/>

42: </listener>

43: </listeners>

You can leave the files specified in these tags alone unless you want to get into thedetails of how Alice recognizes sentence ends, compensates for misspelling, and so on.

44: <!--You can include such things as predicates, substitutions, and

45: sentence-splitters directly here, but it is likely that multiple

46: bots will use the same ones, so it’s more convenient to use the

47: href attribute on such elements, as shown.-->

48: <predicates href=”predicates.xml”/>

49: <substitutions href=”substitutions.xml”/>

50: <sentence-splitters href=”sentence-splitters.xml”/>

51: <!--You may enumerate each file you want the bot to load, or use

52: simple glob-like expressions with “*”.

53: The path is relative to the location of this file.-->

The <learn> tag pairs specify the sources for hard-coded knowledge files.You canupdate and save startup.xml, and the server will incorporate the changes in the knowl-edge base.

54: <learn>../aiml/standard/*.aiml</learn>

55: <learn>../aiml/extensions/*.aiml</learn>

56: <learn>../aiml/*.aiml</learn>

57: </bot>

58: <!--Here is a sample second bot. It uses the same AIML set as the first

59: example, but no listeners are configured. You can enable it by

60: setting its enabled attribute to “true”.-->

61: <bot id=”TestBot-2” enabled=”false”>

62: <properties>

63: <property name=”name” value=”Testy 2”/>

64: <property name=”master” value=”A.L.I.C.E. AI Foundation”/>

65: </properties>

66: <predicates href=”predicates.xml”/>

67: <substitutions href=”substitutions.xml”/>

68: <sentence-splitters href=”sentence-splitters.xml”/>

69: <learn>../aiml/standard/*.aiml</learn>

70: </bot>

71: </bots>

72:</programd-startup>

That’s about all there is to configuring the Alice Server. Just remember to add the<listener> stanza for the AliceJabber mux.

11 0672325365 CH08 1/21/05 2:25 PM Page 305

Page 323: Jabber Developer’s Handbook [Sams 2004]

306 Chapter 8 Jabber and Conversational Software Agents

ALICE Static Knowledge (AIML) FilesThis section describes enough AIML for general understanding and to get you started,should you wish to add knowledge to your chatterbot-based applications.AIML enablesyou to input potential questions and static responses into Alicebots. It also enables you toembed tags to invoke arbitrary external operating system calls (the <system/> tag), andto call ECMAScript methods inline (the <javascript/> tag), returning the result as astring, which is then output to the user.This latter capability is especially handy in Javaversions of the Alicebot server because you can then invoke arbitrary Java classes[2].

Each stanza of AIML provides property desciptions for constructing instances of anobject called an AIML object. Properties may also dictate server logic. (For example,when a range of possible responses is given for a question, the server will know tochoose one randomly.) More generally, the server uses AIML tags to transform a replyinto a script that can save data, activate other programs, give conditional responses, andrecursively call a pattern-matching algorithm to insert the responses from other cate-gories.

The Category TagA stanza of AIML is delimited by a <category>...</category> pair; the categorydefines a basic atom of knowledge in AIML. Each category tag contains an input ques-tion, an output answer, and an optional context.The input question may just be a point-er to another category tag.

The Pattern TagThe question asked by the user and transported via Jabber is contained in a <pattern>...</pattern> pair.The AIML pattern language is simple, consisting only of words,spaces, and two wildcard symbols,“_” and “*”.Words are letters and numerals, but noother characters.What’s contained between <pattern> tags is case agnostic.Words mustbe separated by a single space, and the wildcard characters function like words.

The Template TagThe bot’s response is contained in a <template>...</template> pair.As mentionedpreviously, the response can be either a string or an external call returning a string.Thetemplate can also set internal variables within Alice, as we illustrate later in this chapter.

The Topic TagThe <topic> tag appears outside the category and collects a group of categories togeth-er.The topic may be set inside any template.

2 Alicebot servers are written in a number of different languages—Perl, for example.We’re moreconcerned with demonstrating integration of Jabber with lots of interesting clients rather thandoing an exhaustive Alice reference book, so Java seemed a nice common denominator language. Ifyou do have the time and desire to do an intergration of the Perl-based server and Perl Jabberlibraries, the architectural approach illustrated in Figure 8.10, later in this chapter, is still relevant.

11 0672325365 CH08 1/21/05 2:25 PM Page 306

Page 324: Jabber Developer’s Handbook [Sams 2004]

307ALICE Static Knowledge (AIML) Files

Let’s look at an AIML file. First of all, don’t forget that AIML is XML, so we need the“obligatory” XML identifying information, and for good measure, a version ID forwhich version of AIML we’re using:

<?xml version=”1.0” encoding=”ISO-8859-1”?>

<aiml version=”1.0”>

Here’s a simple question-and-answer pair. If you specifically ask “What isCOUGAAR?” or some variant, you’ll get the response shown in the template.

<category>

<pattern>WHAT IS COUGAAR</pattern>

<template>

Cougaar is a large-scale agent architecture project that originated

under DARPA, and has been made available to the general public through

open source licensing. Cougaar provides developers with a framework to

implement large-scale distributed agent applications with minimal

consideration for the underlying architecture and infrastructure.

</template>

</category>

Figure 8.3 shows a Jabber Client asking that very question in a chat window.

Figure 8.3 Conversing with Alice.

11 0672325365 CH08 1/21/05 2:25 PM Page 307

Page 325: Jabber Developer’s Handbook [Sams 2004]

308 Chapter 8 Jabber and Conversational Software Agents

Note that “Alice” is on the client’s roster, because we enabled that with the AliceJabbermux.The user has chosen “Chat” from Exodus’ context menu, and is seen chatting withan Alicebot.After some “Eliza [3]-style” badinage, all of which is guided by other bits ofAIML oriented toward conducting trivial conversation, the user ignores the shallowattempt at small talk and asks the question,“What’s COUGAAR, please?”The bot hassome rules about question style, one of which can be stated, if you ever see “please” in aconversation, then say something nice about the user’s politeness. It looks like this:

<category>

<pattern>_ PLEASE</pattern>

<template>

Your polite style is very nice.

<think><set name=”personality”>polite</set></think>

</template>

</category>

Notice the <think> tag.As we said before, a template can also set internal variableswithin Alice.Alice has a dictionary property called “personality,” one key of which ispoliteness. In this case,Alice is told to believe that you’re polite.This rule fires before theactual question, so that’s why its response outputs “Your polite style is very nice.”Alicealso has symbolic reduction rules for contractions; that’s why even though the user asked“What’s COUGAAR, please?” the stimulus question matched <pattern>WHAT ISCOUGAAR</pattern>.

Given another pattern, and another question and answer, we see that Alice has a fewother tricks up her (virtual) sleeve, as shown in Figure 8.4.

<category>

<pattern>WHO IS MARK GREAVES</pattern>

<template>Dr. Mark Greaves is the DARPA Program Manager for Ultralog.

</template>

</category>

The user asks “Who the heck is Mark Greaves?” and Alice has a set of random quipsready for the general pattern <pattern>WHO * </pattern>, where “*” is a wild card.This one intercepts and fires because the pattern specified earlier would have matchedon only the specific string,“Who is Mark Greaves?”As you can see, when the questionwas asked again,Alice responded with a remembrance of the previous question, plus thecorrect response.

You can also instruct Alice to make some random choices with the <random> tag, fol-lowed by a list of responses contained in <li>...</li> pairs:

<category>

<pattern>WHO IS DANA MOORE</pattern>

3 Chatterbots like Alice are patterned after Eliza, also called Doctor or Psychiatrist. Eliza was writ-ten in the 1980s in Lisp. Eliza was/is a conversational bot that many people believed was real.More than a few individuals spent many hours in seeking personal help from Eliza.

11 0672325365 CH08 1/21/05 2:25 PM Page 308

Page 326: Jabber Developer’s Handbook [Sams 2004]

309ALICE Static Knowledge (AIML) Files

<template>

<random>

<li>Dana can be contacted at [email protected]</li>

<li>Dana Moore is a Senior Scientist with BBN Technologies in Arlington, VA.</li>

<li>Dana Moore is really cool</li>

<li>Dana Moore is a terrific guitarist</li>

<li>Dana Moore is an excellent volleyball setter</li>

<li>Dana Moore is an adjunct professor</li>

</random>

</template>

</category>

<category>

<pattern>WHO IS BILL WRIGHT</pattern>

<template>

<random>

<li>Bill Wright is a Division Engineer with BBN Technologies in Arlington,VA.</li>

<li>Bill Wright is a nerd</li>

<li>He’s a terrific musician</li>

<li>Bill Wright is an excellent lecturer</li>

</random>

</template>

</category>

Figure 8.4 Identifying a person.

Check out the resulting dialogue in Figure 8.5; we’re certainly glad Alice didn’t callBill a nerd.

11 0672325365 CH08 1/21/05 2:25 PM Page 309

Page 327: Jabber Developer’s Handbook [Sams 2004]

310 Chapter 8 Jabber and Conversational Software Agents

Figure 8.5 Inquiring about your humble authors.

Here’s a pattern that describes our corporation and would enable an AliceBot to respondintelligently to general queries about your company.You should, of course, adjust accord-ingly:

<category>

<pattern>WHAT IS BBN</pattern>

<template>

For over 50 years, the BBN name has been synonymous with technological

innovation. Since implementing and operating the ARPANET, the forerunner

of today’s Internet, we have been responsible for a number of networking

firsts: the first packet switch, the first router, and the first

person-to-person network email. We also designed, built, and operated the

Defense Data Network. Today at BBN, some of the same scientists involved

in those projects are working with the brilliant young graduates of the country’s

top technical schools to pioneer new innovations.

</template>

</category>

Wild-carding is done with an “*”. If you ask Alice anything followed by “BBN,” itwill respond accordingly, as you can see in Figure 8.6.[4]Finally, stimuli don’t have to be in the form of a question [5]:

4 Note that in Figure 8.6,Alice at first claims not to know anything about BBN, then gives thefull dump.We believe that this may be an artifact of the way in which we configured the knowl-edge base and also a program bug.Alice looks in the root folder first for her knowledge. She didn’tfind the pattern there, and therefore added the string I do not know what BBN is. to theresponse buffer. Later, she found the correct response template and added that as well.5 Pattern stimuli don’t need to be in all uppercase.This just seems to be a custom in the Alicecommunity.

11 0672325365 CH08 1/21/05 2:25 PM Page 310

Page 328: Jabber Developer’s Handbook [Sams 2004]

311ALICE Static Knowledge (AIML) Files

Figure 8.6 Asking some more questions.

<category>

<pattern>PYTHON CONFUSES ME</pattern>

<template>

I would expect such a comment from an untrained user!

Python can be learned in under an hour by a competent programmer.

</template>

</category>

AIML currently supports two ways to interface with other languages and systems.The<system> tag executes any program accessible as an operating system shell command andinserts the results in the reply. Similarly, the <javascript> tag allows arbitrary scriptinginside the templates.

The optional context portion of the category consists of two variants, called <that>and <topic>, shown previously.The <that> tag appears inside the category, and its pat-tern must match the robot’s last utterance; it’s how Alice remembered that it had madethat “Tony Blair” crack earlier. Remembering one last utterance is important if the robotasks a question. Here’s an example of an external system call. Note that it’s embeddedwithin the <template> tag.You can use any appropriate executable command line; obvi-ously you want to choose something that returns a result to the command’s system out-put. Here we show a template that responds to questions such as “How is localhostdoing?”

<category>

<pattern>HOW IS LOCALHOST *</pattern>

<template>

The information I have is as follows:

<system>

“c:\cygwin\bin\ps -eaf”

</system>

</template>

</category>

11 0672325365 CH08 1/21/05 2:25 PM Page 311

Page 329: Jabber Developer’s Handbook [Sams 2004]

312 Chapter 8 Jabber and Conversational Software Agents

The template responds,“The information I have is as follows:”, then concatenates thesystem output of the ps command (see Figure 8.7).

Figure 8.7 Inspecting system status.

Note that if you’re inspecting the output directly from the Alice Server, you’ll see theresult of every query, every response, and every system call:

Waiting. . .

Done Waiting.

UID PID PPID TTY STIME COMMAND

Dmoore 568 1 con 11:42:46 /cygdrive/c/JabberD/jabberd

Dmoore 2460 568 con 11:42:47 /cygdrive/c/WINNT/jabadns

Dmoore 2468 1 con 11:51:29 /usr/bin/bash

Dmoore 2596 1 con 16:36:36 /usr/bin/ps

[16:36:36] System process exit value: 0

The Waiting. . . represents the timeout for which the server is willing to wait uponthe execution of the embedded command.The additional output reflects what gets sentto the AliceJabber mux and then forwarded to the Jabber client.

You can no doubt think of better and more relevant (to your job) system calls toembed, but can you imagine how cool it would be to shove a Jabber IM client in frontof your local version of Dilbert’s “pointy haired boss” and typing in something like“What’s the state of Project Wooly Mammoth,” then having an Alicebot respond? If yourpointy hair is like ours, the response would either be to ask,“Who’s Alice? I don’t recallan Alice working for me,” or to continue to do his or her impression of an inanimateobject.At least you will have fun with it, though.

Here’s an example of an ECMAScript call. Notice that the ECMAScript code itself isset off as a blob of CDATA.The response to the stimulus,“What time is it?” first emits“The time is.”Then the embedded Rhino interpreter interprets the script, which firstcreates a Java Date object: var now = new java.util.Date(). It then parses theinstance, getting the hours and minutes, and creates a de facto ECMAScript string,which is the value returned from the method.

</aiml>

<category>

11 0672325365 CH08 1/21/05 2:25 PM Page 312

Page 330: Jabber Developer’s Handbook [Sams 2004]

313ALICE Static Knowledge (AIML) Files

<pattern>WHAT TIME IS IT</pattern>

<template>

The time is <javascript>

<![CDATA[

var now = new java.util.Date()

var hour = now.getHours()

var minute = now.getMinutes()

now = null

var ampm = “”

// validate hour values and set value of ampm

if (hour >= 12) {

hour -= 12

ampm = “PM”

} else

ampm = “AM”

hour = (hour == 0) ? 12 : hour

// add zero digit to a one digit minute

if (minute < 10)

minute = “0” + minute // do not parse this number!

hour + “:” + minute + “ “ + ampm;

]]>

</javascript>

</template>

</category>

</aiml>

The template emits,“The time is ,” followed by the output of the script (see Figure 8.8).

Figure 8.8 Does anybody really know what time it is?

11 0672325365 CH08 1/21/05 2:25 PM Page 313

Page 331: Jabber Developer’s Handbook [Sams 2004]

314 Chapter 8 Jabber and Conversational Software Agents

If you’re looking at the server’s output.You’ll see AliceJabber echoing the messageplus the call to the Rhino interpreter:

[16:38:59] AliceJabber: Message from [dana]: what time is it?

[16:38:59] Log.devinfo: Calling JavaScript interpreter

➥ org.alicebot.server.core.interpreter.RhinoInterpreter

Finally, here’s an ECMAScript that shows Alice nicely demonstrating the capability toextend her knowledge from an outside source.The stimulus will be “lookup word ,” fol-lowed by a single string (see Listing 8.1).

Listing 8.1 ECMAScript to Fetch Outside Data

1:<category>

2:<pattern>LOOKUP WORD *</pattern>

3:<template>

4:

5:<javascript>

6: var word = ‘<star/>’;

7:<![CDATA[

8: var _server = “dict.org”;

9: var _port = 2628;

10: var _socket = java.net.Socket;

11: var _in = java.io.BufferedReader;

12: var _out = java.io.PrintWriter;

13: var _buffer = java.lang.StringBuffer;

14: var _inReader = java.io.InputStreamReader;

15: var _userInput = java.lang.String;

16:

17: _in = null;

18: _out = null;

19: _socket = null;

20:

21:

22: _socket = new java.net.Socket(_server,_port);

23: _socket.setKeepAlive(true);

24: _socket.setSoTimeout(5000);

25: _out = new java.io.PrintWriter(_socket.getOutputStream(), true);

26: _inReader = new java.io.InputStreamReader(_socket.getInputStream());

27: _in = new java.io.BufferedReader(_inReader);

28:

29: _userInput = new java.lang.String();

30: _buffer = new java.lang.StringBuffer();

33: _out.println(“define wn “ + word + “\n\n”);

35: while ((_userInput = _in.readLine()) != null) {

36: if (_userInput.startsWith(“220”)) continue;

11 0672325365 CH08 1/21/05 2:25 PM Page 314

Page 332: Jabber Developer’s Handbook [Sams 2004]

315ALICE Static Knowledge (AIML) Files

37: if (_userInput.startsWith(“151”)) continue;

38: if (_userInput.startsWith(“150”)) continue;

39: if (_userInput.startsWith(“.”)) break;

40: _buffer.append(_userInput + “ “);

41: }

42: _out.close();

43: _in.close();

44: _socket.close();

45:

46: _buffer.toString();

47:

48: s = _buffer.toString() + “.”;

49:]]>

50:</javascript>

51:</template>

52:</category>

In this script, the “*” represents the word to be looked up. It gets substituted into thescript itself (line 2).

Note that you must put AIML tags and any arguments that you want the script toconsume outside the CDATA section (line 6).The script is going to create a socket con-nection to a dictionary site (lines 8-9) and provide word as input to the site.We create asocket on line 22, using the standard java.net.Socket, and multiplex writers and read-ers on top of the socket (lines 25-27).We wait 5 seconds for a response(_socket.setSoTimeout(5000)). If the site returns some good HTTP results, we keepcycling until we get a “.” by itself, at which time we will have collected the definitionand appended it to the StringBuffer _buffer (line 40). Does it work? See for yourself inFigure 8.9.

Listing 8.1 Continued

Figure 8.9 Dictionary bot.

11 0672325365 CH08 1/21/05 2:25 PM Page 315

Page 333: Jabber Developer’s Handbook [Sams 2004]

316 Chapter 8 Jabber and Conversational Software Agents

TipNote that Alice needs to see the “;” at the end of each ECMAScript statement. Failing to provide one will

generate JavaScript errors.

Isn’t distributed computing fun? We have distributed human clients somewhere on theNet talking to a conversational agent somewhere else on the Net, and the agent using aservice on the Net.And the kick is that it’s all seamless from the client’s standpoint.

Fitting the Pieces TogetherWell, that was a lot of non-Jabber discussion, but Alice is complex in its own right.Youcan just install the Alicebot server from http://www.alicebot.org and build upon it byadding your own AIML and using ECMAScript.

If you’re still with us and want to build your own set of services, we’re ready, too.What we’re going to build is depicted in Figure 8.10. Let’s inventory the necessary

downloads first:n You should grab the JabberBeans library, jabberbeans.jar, fromhttp://www.jabberstudio.org. If you want to build it from source, just followthe directions on that site.

n You will need Java 1.4 or above as well.We believe that anything previous maycause problems. Java is available in source or binary form fromhttp://java.sun.com.

n You should get and install the latest Alicebot server (originally called ProgramD).After installing, run it just to verify that it works.You can get it fromhttps://sourceforge.net/projects/charliebot/.You are going to need to getthe complete source because we will be adding a module to it and then recompil-ing the project.

n Of course you need to get and install a jabberd server.You’ve already done that,right? If not, check out Chapter 2,“Installing and Configuring Jabber Software.”

As depicted in the figure, the basic Alice server operates on built-in knowledge suppliedfrom AIML files, whose structure was covered earlier. It also builds in a capability forknowledge extension via the use of ECMAScript. Finally, it accepts listeners to beplugged in.As Figure 8.10 shows, the listener built here will appear to be an ordinaryclient; thus other clients may add “alice” to their rosters as two clients (bill@localhostand dana@localhost) have done by adding a client called alice@localhost to theirrespective rosters.

An Alicebot listener must extend org.alicebot.server.net.listener.AliceChatListener.An AliceChatListener’s constructor consumes an org.alicebot.server.core.Bot, which is the chatterbot for which this listener is valid. Muxes can beadded so long as they extend this relatively simple class.

11 0672325365 CH08 1/21/05 2:25 PM Page 316

Page 334: Jabber Developer’s Handbook [Sams 2004]

317Fitting the Pieces Together

Figure 8.10 Alice-Jabber architecture.

Extending this class permits interaction with an Alice chatterbot, but we haven’t takencare of enabling ordinary Jabber clients to interact with Alice yet. For this, you need toimplement the JabberBeans PacketListener interface to receive notification of newmessages.

The Alice Java code tree resembles this (as of this writing):

org/

org/alicebot/

org/alicebot/gui/

org/alicebot/server/

org/alicebot/server/core/

org/alicebot/server/core/interpreter/

org/alicebot/server/core/loader/

org/alicebot/server/core/logging/

org/alicebot/server/core/node/

org/alicebot/server/core/parser/

org/alicebot/server/core/processor/

Exodus Tools Help

Available

++++ +

Exodus - ... X

Exodus Tools Help

Available

++++ +

JabberBook

alice

caitlin

dana

jane

Friends

caitlin

? jane

JabberBook

alice

bill

Internet Content Providers

ECMAScript

ALICE Server

Built-In Knowledge

MUXes

Jabberd

AliceJabberJabberLib

Client Emulation

Exodus - ... X- -

Exodus - bill@localhost Exodus - dana@localhost

11 0672325365 CH08 1/21/05 2:25 PM Page 317

Page 335: Jabber Developer’s Handbook [Sams 2004]

318 Chapter 8 Jabber and Conversational Software Agents

org/alicebot/server/core/processor/loadtime/

org/alicebot/server/core/responder/

org/alicebot/server/core/targeting/

org/alicebot/server/core/targeting/gui/

org/alicebot/server/core/util/

org/alicebot/server/net/

org/alicebot/server/net/listener/

org/alicebot/server/net/servlet/

org/alicebot/server/sql/

org/alicebot/server/sql/pool/

The AliceJabber Mux CodeListing 8.2 provides the source for the AliceJabber class.

Listing 8.2 The AliceJabber Class

1:package org.alicebot.server.net.listener;

2:

3:import java.net.InetAddress;

4:import java.util.Hashtable;

5:

6:import org.alicebot.server.core.*;

7:import org.alicebot.server.core.logging.Log;

8:import org.alicebot.server.core.responder.TextResponder;

9:import org.jabber.jabberbeans.*;

10:import org.jabber.jabberbeans.Extension.IQAuthBuilder;

11:import org.jabber.jabberbeans.util.*;

12:

13:public class AliceJabber extends AliceChatListener

14: implements PacketListener {

15: private String host, userid, password, resource;

16: private static final String MSG = “AliceJabber: “;

17: public static final String label = “AliceJabber”;

18:

19: private ConnectionBean cb;

20:

21: public AliceJabber(Bot bot) {

22: super(bot, “AliceJabber”, new String[][] { { “host”, “” }, {

23: “userid”, “” }, {

24: “password”, “” }, {

25: “resource”, “” }

26: });

27: }

28:

29: public boolean checkParameters() {

30: try {

11 0672325365 CH08 1/21/05 2:25 PM Page 318

Page 336: Jabber Developer’s Handbook [Sams 2004]

319The AliceJabber Mux Code

31: host = (String) parameters.get(“host”);

32: userid = (String) parameters.get(“userid”);

33: password = (String) parameters.get(“password”);

34: resource = (String) parameters.get(“resource”);

35: } catch (Exception e) {

36: logMessage(e.getMessage());

37: return false;

38: }

39:

40: return true;

41: }

42:

43: public void receivedPacket(PacketEvent pe) {

44: Message msg = (Message) pe.getPacket();

45: String msgFrom = msg.getFromAddress().toString();

46: logMessage(“Message from [“ + msgFrom + “]: “ + msg.getBody());

47: if (msg.getBody() != null) {

48: MessageBuilder mb = new MessageBuilder();

49: mb.setToAddress(msg.getFromAddress());

50: mb.setFromAddress(msg.getToAddress());

51: mb.setSubject(msg.getSubject());

52: mb.setThread(msg.getThread());

53: mb.setType(msg.getType());

54:

55: String botResponse =

56: Multiplexor.getResponse(

57: msg.getBody(),

58: msgFrom,

59: botID,

60: new TextResponder());

61: mb.setBody(botResponse);

62: try {

63: cb.send(mb.build());

64: } catch (InstantiationException e) {

65: }

66: }

67: }

68:

69: public void shutdown() {

70: signoff();

71: }

72:

73: public void run() {

74: try {

75:

Listing 8.2 Continued

11 0672325365 CH08 1/21/05 2:25 PM Page 319

Page 337: Jabber Developer’s Handbook [Sams 2004]

320 Chapter 8 Jabber and Conversational Software Agents

76: cb = new ConnectionBean();

77: SyncPacketListener sync = new SyncPacketListener(cb);

78: Packet p;

79:

80: cb.connect(InetAddress.getByName(host));

81: InfoQueryBuilder iqb = new InfoQueryBuilder();

82: IQAuthBuilder auth = new IQAuthBuilder();

83: iqb.setType(“set”);

84: auth.setUsername(userid);

85: auth.setPassword(password);

86: auth.setResource(resource);

87:

88: iqb.addExtension(auth.build());

89: Packet iq = iqb.build();

90: p = sync.sendAndWait((ContentPacket) iq, 5000);

91:

92: new MessengerBean(cb).addPacketListener(this);

93: RosterBean rosterBean = new RosterBean();

94: rosterBean.setIQBean(new IQBean(cb));

95: rosterBean.refreshRoster();

96: PresenceBean presenceBean = new PresenceBean();

97: presenceBean.setConnBean(cb);

98: presenceBean.addPresenceListener(new OKPresenceListener());

99: PresenceBuilder pb = new PresenceBuilder();

100: pb.setStatus(“Available”);

101: presenceBean.getConnBean().send(pb.build());

102: } catch (Exception e) {

103: }

104: }

105: // Utility class to acknowledge presence subscribe/unsubscribe requests

106: private class OKPresenceListener implements PresenceListener {

107:

108: public void subscribe(Presence p) {

109: reply(p, “subscribed”);

110: reply(p, “subscribe”);

111: }

112: public void unsubscribe(Presence p) {

113: reply(p, “unsubscribed”);

114: reply(p, “unsubscribe”);

115: }

116:

117: public void changedPresence(

118: Hashtable t,

119: Presence p,

120: PresenceUserNode n,

Listing 8.2 Continued

11 0672325365 CH08 1/21/05 2:25 PM Page 320

Page 338: Jabber Developer’s Handbook [Sams 2004]

321The AliceJabber Mux Code

121: String s) {

122: }

123: // Unneeded Jabberbeans PresenceListener methods

124: public void error(Presence p) {

125: }

126: public void subscribed(Presence p) {

127: }

128: public void unsubscribed(Presence p) {

129: }

130:

131: private void reply(Presence p, String message) {

132: try {

133: PresenceBuilder pb = new PresenceBuilder();

134: pb.setFromAddress(p.getToAddress());

135: pb.setToAddress(p.getFromAddress());

136: pb.setType(message);

137: cb.send(pb.build());

138: } catch (InstantiationException e) {

139: e.printStackTrace();

140: }

141: }

142: }

143:

144: public void signoff() {

145: logMessage(“Signing off.”);

146: cb.disconnect();

147: logMessage(“Signed off.”);

148: }

149: private void logMessage(String message) {

150: Log.userinfo(MSG + message, Log.LISTENERS);

151: }

152: public void sendFailed(PacketEvent arg0) {

153: }

154:

155: public void sentPacket(PacketEvent arg0) {

156: }

157:

158:}

It’s important to examine at least a few sections of the code. First, note that you shouldput your code in the package org.alicebot.server.net.listener.

1:package org.alicebot.server.net.listener;

We include the core Bot class, which gets passed to the constructor. Multiplexor isused in the receivedPacket() method, which handles the dialogue with Alice.

Listing 8.2 Continued

11 0672325365 CH08 1/21/05 2:25 PM Page 321

Page 339: Jabber Developer’s Handbook [Sams 2004]

322 Chapter 8 Jabber and Conversational Software Agents

Multiplexor uses a class public method, getResponse(), to manage the dialogue (see thefollowing code). In this case, to “multiplex” means “to select one from many inputs.”TheAlice Multiplexor handles input from a bot’s clients and keeps track of all their predicatevalues.

In short, this class handles a session from the bot’s point of view.

6:import org.alicebot.server.core.*;

7:import org.alicebot.server.core.logging.Log;

8:import org.alicebot.server.core.responder.TextResponder;

It’s also important to import the JabberBeans classes, so a session can be implementedfrom Jabber’s point of view.

9:import org.jabber.jabberbeans.*;

10:import org.jabber.jabberbeans.Extension.IQAuthBuilder;

11:import org.jabber.jabberbeans.util.*;

The receivedPacket callback does the heavy lifting for the class. Notice that theJabberBeans PacketListener interface is implemented. It’s going to handle the incomingmessage from the Jabber client, wait on Alice’s response, and send that back to the Jabberuser.The sender’s Jabber ID is extracted from the Jabber message object (line 45,) whichwill be used to feed the Multiplexor with the sender information it needs.TheTextResponder processes and logs input via a given channel (text, HTML, Flash, and soon) on behalf of the Alicebot.This handling is partially asynchronous and partly synchro-nous. receivedPacket is an asynchronous callback. getResponse waits synchronously onAlice’s answer; the ConnectionBean (line 63) turns the response around to the Jabberclient.

43: public void receivedPacket(PacketEvent pe) {

44: Message msg = (Message) pe.getPacket();

45: String msgFrom = msg.getFromAddress().toString();

46: logMessage(“Message from [“ + msgFrom + “]: “ + msg.getBody());

47: if (msg.getBody() != null) {

48: MessageBuilder mb = new MessageBuilder();

49: mb.setToAddress(msg.getFromAddress());

50: mb.setFromAddress(msg.getToAddress());

51: mb.setSubject(msg.getSubject());

52: mb.setThread(msg.getThread());

53: mb.setType(msg.getType());

54:

55: String botResponse =

56: Multiplexor.getResponse(

57: msg.getBody(),

58: msgFrom,

59: botID,

60: new TextResponder());

61: mb.setBody(botResponse);

62: try {

11 0672325365 CH08 1/21/05 2:25 PM Page 322

Page 340: Jabber Developer’s Handbook [Sams 2004]

323The AliceJabber Mux Code

63: cb.send(mb.build());

64: } catch (InstantiationException e) {

65: }

66: }

67: }

Here’s the setup from Jabber’s standpoint: Declare a new ConnectionBean, then regis-ter this class as the PacketListener (lines 76, 92).The receivedPacket method alreadyshown does the job of handling messages.The ConnectionBean object connects to theJabber Server (line 80), authenticates (lines 81–90), gets the roster of users (lines 93–95),and in return, advertises our presence (lines 96–101). Notice the use of a presence listen-er to respond to presence subscription requests (line 98).

73: public void run() {

74: try {

75:

76: cb = new ConnectionBean();

77: SyncPacketListener sync = new SyncPacketListener(cb);

78: Packet p;

79:

80: cb.connect(InetAddress.getByName(host));

81: InfoQueryBuilder iqb = new InfoQueryBuilder();

82: IQAuthBuilder auth = new IQAuthBuilder();

83: iqb.setType(“set”);

84: auth.setUsername(userid);

85: auth.setPassword(password);

86: auth.setResource(resource);

87:

88: iqb.addExtension(auth.build());

89: Packet iq = iqb.build();

90: p = sync.sendAndWait((ContentPacket) iq, 5000);

91:

92: new MessengerBean(cb).addPacketListener(this);

93: RosterBean rosterBean = new RosterBean();

94: rosterBean.setIQBean(new IQBean(cb));

95: rosterBean.refreshRoster();

96: PresenceBean presenceBean = new PresenceBean();

97: presenceBean.setConnBean(cb);

98: presenceBean.addPresenceListener(new OKPresenceListener());

99: PresenceBuilder pb = new PresenceBuilder();

100: pb.setStatus(“Available”);

101: presenceBean.getConnBean().send(pb.build());

102: } catch (Exception e) {

103: }

If you keep the default Alicebot configuration, just put the aaa-jabberbook.aiml filein the AIML/standard directory of your Alice installation.The server will find the filethere and load it when it starts.

11 0672325365 CH08 1/21/05 2:25 PM Page 323

Page 341: Jabber Developer’s Handbook [Sams 2004]

324 Chapter 8 Jabber and Conversational Software Agents

To add the AliceJabber listener to your Alice server, you need to edit the classorg.alicebot.server.net.listener. AliceChatListenerRegistry in the ProgramDdistribution. Find the PROCESSOR_LIST variable and add the AliceJabber classname to thelist like this:

private static final String[] PROCESSOR_LIST = {

“org.alicebot.server.net.listener.AliceAIM”,

“org.alicebot.server.net.listener.AliceICQ”,

“org.alicebot.server.net.listener.AliceJabber”,

“org.alicebot.server.net.listener.AliceIRC”};

As we mentioned earlier, to add the AliceJabber listener to your Alice server, addthese lines to the <listeners> section of startup.xml:

<listener type=”AliceJabber” enabled=”true”>

<parameter name=”host” value=”my-jabber”/>

<parameter name=”userid” value=”alice”/>

<parameter name=”password” value=”alice”/>

<parameter name=”resource” value=”alice”/>

</listener>

This tells the Alice server about the new listener and configures the Jabber connec-tion values.You need to change the host, userid, password, and resource values to matchyour installation, of course.

The AIML in the example uses JavaScript, so you need to edit the server.props fileto enable it. Search for the main ProgramD configuration section and update this entry:

# Allow use of <javascript> element? (true/false)

programd.javascript-allowed=true

Finally, your Alicebot is pretty stupid without the default set of AIML knowledgefiles.You need to download the standard set from http://www.alicebot.org/downloads/ and install them in the aiml/standard directory, along with the aaa-jabberbook.aiml file.

Running ALICE with JabberWhen you build your custom AliceJabber Server, then start it (the jabberd should berunning first), you’ll see something like this:

Starting Alicebot Program D.

[13:21:51] Starting Alicebot Program D version 4.1.5

[13:21:51] Using Java VM 1.4.0_01-b03 from Sun Microsystems Inc.

[13:21:51] On Windows 2000 version 5.0 (x86)

[13:21:51] Predicates with no values defined will return: “undefined”.

[13:21:52] Initializing Multiplexor.

[13:21:52] Loading Graphmaster.

[13:21:52] Starting up with “C:\AliceX\ProgramD\conf\startup.xml”.

[13:21:52] Configuring bot “Alice”.

11 0672325365 CH08 1/21/05 2:25 PM Page 324

Page 342: Jabber Developer’s Handbook [Sams 2004]

325Summary

[13:21:52] Started “AliceJabber” listener for bot “Alice”.

[13:21:52] Loaded 287 input substitutions.

Connection event: org.jabber.jabberbeans.ConnectionEvent[source=

➥ org.jabber.jabberbeans.ConnectionBean@587c94]

[13:21:52] Loaded 19 gender substitutions.

[13:21:52] Loaded 9 person substitutions.

[13:21:52] Loaded 60 person2 substitutions.

[13:21:52] Loaded 4 sentence-splitters.

[13:21:55] 6000 categories loaded so far.

[13:21:57] 12000 categories loaded so far.

[13:22:00] 18000 categories loaded so far.

[13:22:01] 1 bots thinking with 22013 categories.

[13:22:01] Alicebot Program D (c) 1995-2002 A.L.I.C.E. AI Foundation

[13:22:01] All Rights Reserved.

[13:22:01] This program is free software; you can redistribute it and/or

[13:22:01] modify it under the terms of the GNU General Public License

[13:22:01] as published by the Free Software Foundation; either version 2

[13:22:01] of the License, or (at your option) any later version.

[13:22:01] Alicebot Program D version 4.1.5 Build [00]

[13:22:01] 22013 categories loaded in 9.173 seconds.

[13:22:01] The AIML Watcher is not active.

[13:22:01] HTTP server listening at http://empath:2001

[13:22:02] Interactive shell: type “/exit” to shut down; “/help” for help.

[13:22:02] Alice> Hello there Dana and thanks for connecting!

[13:22:02] [Alice] Dana>

Notice that in addition to loading the static knowledge base,Alice now starts theAlice Jabber mux and connects to Jabber.

There are actually commercial implementations that use an approach similar to theone you’ve just built, including one (www.verbots.com) that uses Alice, plus text-to-speech output and animated on-screen avatars to interact with a user.

SummaryThe combination of the Alicebot and the Jabber protocols provides a really interestingway for humans and computers to interact.These two open source technologies seemmade for each other and their applications are limited only by our imagination.

11 0672325365 CH08 1/21/05 2:25 PM Page 325

Page 343: Jabber Developer’s Handbook [Sams 2004]

11 0672325365 CH08 1/21/05 2:25 PM Page 326

Page 344: Jabber Developer’s Handbook [Sams 2004]

9Jabber and System Control

and Administration

I’m just a technical supervisor who cared too much.

Homer Simpson

LOTS OF TOOLS ARE AVAILABLE for monitoring and controlling computers, but theytend to be very tightly coupled with the systems to be monitored.Tools for Windowssystems don’t generally talk to tools for Unix systems, for example.When thinking abouthow to control a large set of computer systems, several difficulties come immediately tomind:

n A person can’t sit in front of each one and watch for problems—problems need tobe brought to the attention of the operator automatically.

n Very often, the same operation will need to be carried out on all the computers atonce—the operator needs to be able to express,“Do this to these computers.”

n These computers may not be homogeneous, so any approach to managing themcan’t rely on tools that are limited to one operating system or hardware platform.

Those of us who have to manage large networks of computers know that there is nopanacea for these problems, but this chapter presents some ideas and examples of how touse Jabber to address them.

Jabber for System Event MonitoringMicrosoft Windows and Unix (in its various flavors) have mechanisms for applicationprograms and the operating system to log events that might be of interest to the system

12 0672325365 CH09 1/21/05 2:25 PM Page 327

Page 345: Jabber Developer’s Handbook [Sams 2004]

328 Chapter 9 Jabber and System Control and Administration

administrator.These events might be things such as a user login, a service starting orstopping, or a device coming online.Although the concept of event logging is commonto both Windows and Unix, they implement them quite differently. On Windows, youuse the Event Viewer to examine the event logs. Figure 9.1 shows what the WindowsEvent Viewer looks like.

Figure 9.1 The Windows Event Viewer.

The Event Viewer contains timestamped entries that say what happened and what com-ponent is logging the event. Contrast this with the Unix event logger (called “syslog”),which can be configured to send log events to various places, but they are commonlyjust written to a file called something like /var/log/messages. Like the Windows eventlog, this file contains timestamped lines that contain whatever information the compo-nent doing the logging thought was important. Here’s an example of a Unix syslog file:

# cat /var/log/messages

Feb 6 04:04:26 al su(pam_unix)[15690]: session opened for user news by (uid=0)

Feb 6 04:04:27 al su(pam_unix)[15690]: session closed for user news

Feb 7 04:02:42 al su(pam_unix)[20879]: session opened for user news by (uid=0)

Feb 7 04:02:42 al su(pam_unix)[20879]: session closed for user news

Feb 7 15:06:59 al login(pam_unix)[23156]: session opened for user wwright

Feb 7 15:06:59 al -- wwright[23156]: LOGIN ON pts/0 BY wwright FROM ad

Feb 7 15:07:09 al su(pam_unix)[23203]: authentication failure; logname=wwright

Feb 7 15:07:11 al login(pam_unix)[23156]: session closed for user wwright

Feb 7 15:07:14 al su(pam_unix)[23204]: authentication failure; logname=wwright

Feb 7 15:07:24 al su(pam_unix)[23205]: session opened for user root by wwright

Rather than manually check each of these logs, it would be nice if you could have aservice that would watch them for you and send an instant message if an event occurred.Fortunately, Jabber provides a way to unify these disparate logging systems and delivermessages where they need to go.

The next example shows a Jabber client that accepts presence subscriptions fromother Jabber clients and forwards them any system events that occur. Let’s look first atthe client’s view of this service and then go through the code.

12 0672325365 CH09 1/21/05 2:25 PM Page 328

Page 346: Jabber Developer’s Handbook [Sams 2004]

329Jabber for System Event Monitoring

An instant messaging client can subscribe to the presence of the system logger servicejust like any other client. Figure 9.2 shows the system logger as a contact in this user’sroster.

Figure 9.2 A roster containing the system logger.

The presence information says that the service is up and running, ready to send logevents as they happen.When an event occurs, the service formats the information into aJabber message and sends it to all clients who have subscribed to the service (that is, allJabber clients who have exchanged presence subscriptions with the system logger client).These messages appear as incoming messages at the IM client where the user can viewthem. Figure 9.3 shows an event from a Windows system.

Figure 9.3 A Windows system event.

12 0672325365 CH09 1/21/05 2:25 PM Page 329

Page 347: Jabber Developer’s Handbook [Sams 2004]

330 Chapter 9 Jabber and System Control and Administration

Because the Jabber protocols are platform independent, we can treat Unix syslog mes-sages exactly the same.The next figure shows a message from a Unix system reporting asystem event.

Figure 9.4 A Unix system event.

So, important messages from many different kinds of computers can be collected in theone interface presented by the IM client.This makes it much easier to keep track of sev-eral computers at once without missing any important events.

Because the ways system events are manifested on Windows and Unix are so differ-ent, a flexible language such as Python is ideal. Reading the Unix syslog is easy becauseit’s just written to a file. Syslog can be configured to send its messages other places (to aTCP socket, for example), but we’ll stick with a simple file for now.

Reading the Windows event log requires the use of a fairly obscure part of the Win32API, so we’ll use the win32all Python extension available from http://www.python.orgor as part of the ActiveState ActivePython distribution at http://www.activestate.com.The win32all extensions wrap much of the Win32 C API and expose it as Pythonfunctions.

The script in Listing 9.1 is the complete implementation of the system log service forWindows and UNIX.We’ll present the code in its entirety here and then go through itpiece by piece.

Listing 9.1 The System Logger Service

1:import traceback

2:import re

3:import time

4:import string

5:import threading

12 0672325365 CH09 1/21/05 2:25 PM Page 330

Page 348: Jabber Developer’s Handbook [Sams 2004]

331Jabber for System Event Monitoring

6:import sys

7:import jabber

8:import socket

9:

10:# Some imports needed for windows only

11:if sys.platform == “win32”:

12: import win32evtlog

13: import win32api, win32con

14: import win32evtlogutil

15: import win32event

16: event_dict={win32con.EVENTLOG_AUDIT_FAILURE:’AUDIT FAILURE’,\

17: win32con.EVENTLOG_AUDIT_SUCCESS:’AUDIT SUCCESS’,\

18: win32con.EVENTLOG_INFORMATION_TYPE:’INFORMATION’,\

19: win32con.EVENTLOG_WARNING_TYPE:’WARNING’,\

20: win32con.EVENTLOG_ERROR_TYPE:’ERROR’}

21: win32 = True

22:else:

23: win32 = False

24:

25:

26:# convert a windows date string to unix epoch time

27:def date2sec(evt_date):

28: regexp=re.compile(‘(.*)\\s(.*)’)

29: reg_result=regexp.search(evt_date)

30: date=reg_result.group(1)

31: the_time=reg_result.group(2)

32: (mon,day,yr)=map(lambda x: string.atoi(x),string.split(date,’/’))

33: (hr,min,sec)=map(lambda x: string.atoi(x),string.split(the_time,’:’))

34: tup=[yr,mon,day,hr,min,sec,0,0,0]

35: sec=time.mktime(tup)

36: return sec

37:

38:

39:class ReaderThread (threading.Thread) :

40: def __init__ (self, log) :

41: threading.Thread.__init__(self)

42: self.log = log

43: if win32:

44: self.ev_handle=win32evtlog.OpenEventLog(None, log)

45: self.wait_handle=win32event.CreateEvent(None, 0, 0, None)

46: self.die_handle=win32event.CreateEvent(None, 0, 0, None)

47: self.flags = win32evtlog.EVENTLOG_BACKWARDS_READ|\

48: win32evtlog.EVENTLOG_SEQUENTIAL_READ

49:

50: def run (self):

Listing 9.1 Continued

12 0672325365 CH09 1/21/05 2:25 PM Page 331

Page 349: Jabber Developer’s Handbook [Sams 2004]

332 Chapter 9 Jabber and System Control and Administration

51: try:

52: self.alive = True

53: if win32:

54: self.goWin32()

55: else:

56: self.goUnix()

57: except:

58: print traceback.print_exc(sys.exc_info())

59: self.die()

60:

61: def die(self):

62: print “die() called”

63: self.alive = False

64: if win32:

65: win32event.SetEvent(self.die_handle)

66:

67: # event reader loop for windows

68: def goWin32(self):

69: # Make sure this is a version of NT

70: if win32api.GetVersion() & 0x80000000:

71: print “This Client requires Windows NT”

72: return

73:

74: computerName = None

75: log = self.log

76: win32evtlog.NotifyChangeEventLog(self.ev_handle, self.wait_handle)

77: numRecords = win32evtlog.GetNumberOfEventLogRecords(self.ev_handle)

78: events = win32evtlog.ReadEventLog(self.ev_handle, self.flags, 0)

79: print “We ate %d records on startup” % numRecords

80: lastTime = time.time()

81: print “We started at time %d” % lastTime

82: while 1:

83: temp_ev_handle=win32evtlog.OpenEventLog(None, log)

84: numRecords = win32evtlog.GetNumberOfEventLogRecords(temp_ev_handle)

85: rc = win32event.WaitForMultipleObjects(

86: (self.die_handle,self.wait_handle),

87: 0, win32event.INFINITE)

88: if rc==win32event.WAIT_OBJECT_0:

89: print “Asked to die...”

90: break;

91: events = win32evtlog.ReadEventLog(temp_ev_handle, self.flags, 0)

92: if not events:

93: break

94: greatestTime = lastTime;

95: for event in events:

Listing 9.1 Continued

12 0672325365 CH09 1/21/05 2:25 PM Page 332

Page 350: Jabber Developer’s Handbook [Sams 2004]

333Jabber for System Event Monitoring

96: the_time=event.TimeGenerated.Format()

97: seconds=date2sec(the_time)

98: if seconds <= lastTime : break # No more records

99: # event is new

100: self.sendEvent(event)

101: greatestTime = max(seconds, greatestTime)

102: lastTime = greatestTime

103: win32evtlog.CloseEventLog(temp_ev_handle)

104:

105: #event reader loop for unix

106: def goUnix(self):

107: log = self.log

108: file = open(log, ‘r’)

109: file.seek(0, 2) # seek to the end

110:

111: print “We ate %d bytes on startup” % file.tell()

112: while self.alive:

113: line = file.readline()

114: if line == “”:

115: time.sleep(1)

116: else:

117: myMessage = socket.gethostname()+”:”+evt_type+”:”+msg

118: self.sendToAll(“System Event”, myMessage)

119:

120: # parse a windows event into a string

121: def sendEvent(self,event):

122: the_time=event.TimeGenerated.Format()

123: computer=str(event.ComputerName)

124: evt_type=str(event_dict[event.EventType])

125: msg = str(win32evtlogutil.SafeFormatMessage(event, self.log))

126: myMessage = computer+”:”+evt_type+”:”+msg

127: self.sendToAll(“System Event”, myMessage)

128:

129: def sendToAll(self, subject, body):

130: jids = con.getRoster().getJIDs()

131: for jid in jids:

132: msg = jabber.Message(to=jid)

133: msg.setSubject(subject)

134: msg.setBody(body)

135: con.send(msg)

136:

137:# Handle Jabber presence messages

138:def presenceCB(con, prs):

139: who = str(prs.getFrom())

140: type = prs.getType()

Listing 9.1 Continued

12 0672325365 CH09 1/21/05 2:25 PM Page 333

Page 351: Jabber Developer’s Handbook [Sams 2004]

334 Chapter 9 Jabber and System Control and Administration

141: if type == None: type = ‘available’

142:

143: if type == ‘subscribe’:

144: con.send(jabber.Presence(to=who, type=’subscribed’))

145: con.send(jabber.Presence(to=who, type=’subscribe’))

146:

147: elif type == ‘unsubscribe’:

148: con.send(jabber.Presence(to=who, type=’unsubscribed’))

149: con.send(jabber.Presence(to=who, type=’unsubscribe’))

150:

151:def disconnectedCB(con):

152: sys.exit(1)

153:

154:def messageCB(con, msg):

155: print “Unexpected message received: “, msg.getBody()

156:

157:def startJabber(server, user, password, resource):

158: con = jabber.Client(host=server)

159: try:

160: con.connect()

161: con.setMessageHandler(messageCB)

162: con.setPresenceHandler(presenceCB)

163: con.setDisconnectHandler(disconnectedCB)

164:

165: if not con.auth(user, password, resource):

166: print “Jabber login failed: %s” % con.lastErr

167: sys.exit(1)

168:

169: print “Connected to Jabber”

170: con.requestRoster()

171: con.sendInitPresence()

172:

173: except:

174: print “Exception connecting to Jabber”

175: sys.exit(1)

176: return con

177:

178:

179:try:

180: if __name__==’__main__’:

181:

182: if len(sys.argv) != 4:

183: print “usage: “,sys.argv[0], “ <server> <user> <password>”

184: sys.exit(1)

185:

Listing 9.1 Continued

12 0672325365 CH09 1/21/05 2:25 PM Page 334

Page 352: Jabber Developer’s Handbook [Sams 2004]

335Jabber for System Event Monitoring

186: server = sys.argv[1]

187: user = sys.argv[2]

188: password = sys.argv[3]

189:

190: resource = “syslog”

191:

192: con = startJabber(server, user, password, resource)

193: if win32:

194: log_name = “Application”

195: else:

196: log_name = “/var/log/messages”

197: log_thread = ReaderThread(log_name)

198: log_thread.start()

199: while 1:

200: con.process(1)

201:except KeyboardInterrupt:

202: print traceback.print_exc(sys.exc_info())

203: log_thread.die()

204: sys.exit()

The first few lines of this example import the Python modules that will be needed toread either the Windows or the Unix system log. Starting at line 10, we import theWin32 API wrappers and define some variables that will be useful later in the script fordiscriminating between the two platforms.

Because this service has to read from the event log and handle Jabber packets at thesame time, we need to build a multi-threaded script. Starting at line 39, we define a classthat extends the Python thread class, so it can run independent of the main thread ofcontrol. Its constructor is repeated here for convenience.

39:class ReaderThread (threading.Thread) :

40: def __init__ (self, log) :

41: threading.Thread.__init__(self)

42: self.log = log

43: if win32:

44: self.ev_handle=win32evtlog.OpenEventLog(None, log)

45: self.wait_handle=win32event.CreateEvent(None, 0, 0, None)

46: self.die_handle=win32event.CreateEvent(None, 0, 0, None)

47: self.flags = win32evtlog.EVENTLOG_BACKWARDS_READ|\

48: win32evtlog.EVENTLOG_SEQUENTIAL_READ

Here, we store the name of the log in self.log (line 42) and, if this is Windows (line43), initialize the necessary Win32 objects. Line 44 actually opens the event log (return-ing a Win32 handle).We need two more Win32 handles to control the event-readingthread: self.wait_handle (line 45) will be signaled when there is a new entry in theevent log; self.die_handle will be signaled when the client should exit.You can see in

Listing 9.1 Continued

12 0672325365 CH09 1/21/05 2:25 PM Page 335

Page 353: Jabber Developer’s Handbook [Sams 2004]

336 Chapter 9 Jabber and System Control and Administration

the following code that the main loop of this thread spends most of its time waiting onthese two handles.

The next method in the ReaderThread class, called run, is the method that is calledin the new thread when it is started.

50: def run (self):

51: try:

52: self.alive = True

53: if win32:

54: self.goWin32()

55: else:

56: self.goUnix()

57: except:

58: print traceback.print_exc(sys.exc_info())

59: self.die()

The run() method just delegates to one of two other methods, depending on theplatform. For this simple example, if an exception is raised, the script just aborts (lines57–59); a robust script would try to recover.

The goWin32() method (lines 68–103) loops forever reading the Windows event log.The first lines of the method (76–81) read and discard the events that are already in thelog so only new events are reported—that is, those events that happen after the servicestarts.

76: win32evtlog.NotifyChangeEventLog(self.ev_handle, self.wait_handle)

77: numRecords = win32evtlog.GetNumberOfEventLogRecords(self.ev_handle)

78: events = win32evtlog.ReadEventLog(self.ev_handle, self.flags, 0)

79: print “We ate %d records on startup” % numRecords

80: lastTime = time.time()

81: print “We started at time %d” % lastTime

Line 76 tells the Win32 system that it should signal the self.wait_handle eventobject when a new log entry appears in the log.We’ll use this handle to wake up thethread when a system event occurs. It’s necessary to keep track of the time of the latestevent (line 80) so that when new events are logged we can tell the old ones from thenew ones. Next comes the thread’s main loop, which waits for either a new event in theevent log (self.wait_handle is signaled) or the main thread to request that the threadexit by calling the die() method (self.die_handle is signaled).This code waits for asignal on either of the two event objects:

85: rc = win32event.WaitForMultipleObjects(

86: (self.die_handle,self.wait_handle),

87: 0, win32event.INFINITE)

88: if rc==win32event.WAIT_OBJECT_0:

89: print “Asked to die...”

90: break;

Line 88 determines whether the signal was on the die_handle or the wait_handle.If the expression at line 88 is false, then the event log must have changed, so line 91

12 0672325365 CH09 1/21/05 2:25 PM Page 336

Page 354: Jabber Developer’s Handbook [Sams 2004]

337Jabber for System Event Monitoring

reads the event log and lines 95–101 loop through it looking for any events that hap-pened after the last time events were reported.

91: events = win32evtlog.ReadEventLog(temp_ev_handle, self.flags, 0)

92: if not events:

93: break

94: greatestTime = lastTime;

95: for event in events:

96: the_time=event.TimeGenerated.Format()

97: seconds=date2sec(the_time)

98: if seconds <= lastTime : break # No more records

99: # event is new

100: self.sendEvent(event)

101: greatestTime = max(seconds, greatestTime)

102: lastTime = greatestTime

103: win32evtlog.CloseEventLog(temp_ev_handle)

Line 100 calls self.sendEvent(), which generates the Jabber messages for subscrib-ing clients.You can see that method in the following code.

The method that looks for log events under Unix (lines 105–118) is similar but muchsimpler.

105: #event reader loop for unix

106: def goUnix(self):

107: log = self.log

108: file = open(log, ‘r’)

109: file.seek(0, 2) # seek to the end

110:

111: print “We ate %d bytes on startup” % file.tell()

112: while self.alive:

113: line = file.readline()

114: if line == “”:

115: time.sleep(1)

116: else:

117: myMessage = socket.gethostname()+”:”+line

118: self.sendToAll(“System Event”, myMessage)

To get rid of the old entries, this section of the script just opens the log file and seeksto the end (lines 108–109).Then, as long as die() hasn’t been called, it tries to read thefile (line 113). If nothing is read (line 114), the script waits a second and tries again. If itdoes read a line, the line is formatted and sent in a Jabber message (line 118).

NOTEA nice addition to this example would be the capability to filter event messages based on their content or

source. That way you could give special attention to important events such as repeated login failures or disk

full events.

12 0672325365 CH09 1/21/05 2:25 PM Page 337

Page 355: Jabber Developer’s Handbook [Sams 2004]

338 Chapter 9 Jabber and System Control and Administration

Jabber for Version ManagementAs you saw in Chapter 3,“Jabber Client Protocol,” the Jabber protocol includes elementsfor managing software versioning, including facilities for querying the version of Jabberclients and services as well as a protocol for notification of software updates.These ele-ments enable you to manage changing software on a large set of computers.

Jabber clients and components can report their version information in response to<iq> packets in the jabber:iq:version namespace.The following packet is an exampleof one of these IQ packets:

<iq type=”get” id=”wj_2” to=”my-jabber”>

<query xmlns=”jabber:iq:version”>

</query>

</iq>

The destination JID in this packet (“my-jabber” here) contains only a hostname, sothis packet is routed to the JSM component of the server itself, which responds with apacket like this:

<iq type=’result’ id=’wj_2’ to=’user1@my-jabber/Exodus’ from=’my-jabber’>

<query xmlns=’jabber:iq:version’>

<name>jsm</name>

<version>1.4.2</version>

<os>Linux 2.4.18-14</os>

</query>

</iq>

This response includes three elements:n <name>—The name of the remote software. In this case, the jsm component is

responding.n <version>—The version number of the remote client software.The server version

here is 1.4.2.n <os>—The name of the operating system on which the remote software is run-

ning.This server is running on a Linux operating system.

Jabber clients can also respond to version requests. Most instant message clients have aninterface to request version information of other clients. Figure 9.5 shows an examplefrom WinJab.

Right-clicking on a roster contact brings up the menu shown in Figure 9.5.WhenClient Version is selected,WinJab sends this IQ packet to user2:

<iq type=”get” id=”wj_3” to=”user2@my-jabber/Winjab”>

<query xmlns=”jabber:iq:version”>

</query>

</iq>

It’s exactly the same as the packet to the server, except it contains a “to” JID with ausername, host, and resource. User2’s client receives this packet and responds with itsversion information in a packet like this:

12 0672325365 CH09 1/21/05 2:25 PM Page 338

Page 356: Jabber Developer’s Handbook [Sams 2004]

339Jabber for Version Management

<iq type=’result’ id=’wj_3’ to=’user1@my-jabber/Exodus’

from=’user2@my-jabber/Winjab’>

<query xmlns=’jabber:iq:version’>

<name>Winjab</name>

<version>1.1.0.1</version>

<os>NT 5.0</os>

</query>

</iq>

Figure 9.5 Requesting version information from WinJab.

Clients and components can also query other clients and components for time-of-dayinformation by using IQ packets in the jabber:iq:time namespace.This packet queriesa client for the time-of-day at its location:

<iq type=”get” id=”wj_4” to=”user2@my-jabber/Winjab”>

<query xmlns=”jabber:iq:time”>

</query>

</iq>

Like the version query, the get packet contains just an empty query element with theappropriate xmlns attribute.The receiving client responds with a packet like this, con-taining its time of day information:

<iq type=’result’ id=’wj_4’ to=’user1@my-jabber/Exodus’

from=’user2@my-jabber/Winjab’>

<query xmlns=’jabber:iq:time’>

<utc>20030208T14:45:49</utc>

12 0672325365 CH09 1/21/05 2:25 PM Page 339

Page 357: Jabber Developer’s Handbook [Sams 2004]

340 Chapter 9 Jabber and System Control and Administration

<tz>Eastern Standard Time</tz>

<display>2/8/2003 9:45:49 AM</display>

</query>

</iq>

The response includes three elements:n <utc>—Gives the client’s time in ISO 8601 time format, which is 4 digits for the

year, 2 digits for the month, 2 digits for the day, followed by the letter T.That isfollowed by 2 digits for the hour (24-hour format), a colon (:), 2 digits for theminute, another colon (:) and 2 digits for the second.This time is in UniversalCoordinated Time (sometimes known as Greenwich Mean Time or GMT).

n <tz>—The name of the local time zone.n <display>—A human-readable version of the local time.The format here is

arbitrary.

Here’s an example of a Python method that responds to time and version requests.

Listing 9.2 Responding to Time and Version Requests

1:def iqCB(con, msg):

2: query_ns = msg.getQuery()

3: if query_ns == “jabber:iq:version”:

4: reply = jabber.Iq(to=msg.getFrom())

5: reply.setFrom(msg.getTo())

6: reply.setType(“result”)

7: reply.setID(msg.getID())

8: reply.setQuery(query_ns)

9: node = reply.getQueryNode()

10: node.insertXML(“<name>Version Demo</name>”)

11: node.insertXML(“<version>1.0</version>”)

12: node.insertXML(“<os>%s</os>” % sys.platform )

13: con.send(reply)

14: elif query_ns == “jabber:iq:time”:

15: reply = jabber.Iq(to=msg.getFrom())

16: reply.setFrom(msg.getTo())

17: reply.setType(“result”)

18: reply.setID(msg.getID())

19: reply.setQuery(query_ns)

20: t = time.localtime()

21: tz = time.tzname[t[8]]

22: disp = time.strftime(“%a, %d %b %Y %H:%M:%S”, t)

23: utc = time.strftime(“%Y%m%dT%H:%M:%S”, time.gmtime())

24: node = reply.getQueryNode()

25: node.insertXML(“<utc>%s</utc>” % utc)

26: node.insertXML(“<tz>%s</tz>” % tz)

27: node.insertXML(“<display>%s</display>” % disp )

28: con.send(reply)

12 0672325365 CH09 1/21/05 2:25 PM Page 340

Page 358: Jabber Developer’s Handbook [Sams 2004]

341Jabber for Distributed Control

The first section of this method (lines 3–13) handles version requests. Lines 10–12 fill inthe three elements of the version response, using hard-coded strings for the first two andthe python sys.platform value for the operating system.The packet sent by thismethod at line 13 looks like this:

<iq to=’user1@my-jabber/Exodus’ from=’version@my-jabber/exec’ id=’wj_5’

type=’result’>

<query xmlns=’jabber:iq:version’>

<name>Version Demo</name>

<version>1.0</version>

<os>win32</os>

</query>

</iq>

The next section of the method in listing 9.2 (lines 14–28) respond to jabber:iq:time requests. Lines 20–23 use the Python time functions to get the three pieces of datanecessary to fill in the three elements.A packet sent by this method at line 28 looks likethis:

<iq to=’user1@my-jabber/Exodus’ from=’syslog@my-jabber/exec’ id=’wj_7’

type=’result’>

<query xmlns=’jabber:iq:time’>

<utc>20030208T15:24:11</utc>

<tz>Eastern Standard Time</tz>

<display>Sat, 08 Feb 2003 10:24:11</display>

</query>

</iq>

The <utc> element is formatted according to ISO 8601, but the format of the othertwo fields is arbitrary. Including code to handle version and time information is easy andshould be part of any robust client.

Jabber for Distributed ControlIn addition to just monitoring computers, you can use Jabber to conveniently controlsets of computers. If you run a Jabber client on each of the computers to be controlled,it can act as your agent to execute whatever commands you choose. Let’s look at anexample client that can execute commands remotely under control of an IM client.Toallow easy manipulation of banks of computers, this client also joins a Jabber chat roomso a single instruction can be given to all computers at once.

To see how it works, imagine that you have a set of computers (two are shown here)that you’d like to be able to control with arbitrary system commands. First, you start theremote exec client on each computer, then you can add them to your IM client roster.

CautionThis example does not address the security considerations of allowing arbitrary commands to be remotely

executed on your computers. It is meant as an illustration of the techniques involved, which you should not

deploy to production systems unless you understand these security implications.

12 0672325365 CH09 1/21/05 2:25 PM Page 341

Page 359: Jabber Developer’s Handbook [Sams 2004]

342 Chapter 9 Jabber and System Control and Administration

Figure 9.6 shows an IM client subscribed to two system clients.This configuration hasone Unix system and one Windows system, just for variety.

Figure 9.6 An IM client subscribed to remote systems.

From the IM client, you can start a chat session with one of the systems, send it shellcommands, and receive the output in the chat window. Figure 9.7 shows some interac-tion with the Unix system.

Figure 9.7 Chatting with a Unix system.

The commands from the IM client to the remote system are shown in the chat windowprefixed with the username <user1>.The responses from the remote system are prefixedwith its nickname: <Unix System>. In this session, the remote system has been sent somecommon Unix commands so you can see what’s going on. It was asked for its hostname,

12 0672325365 CH09 1/21/05 2:25 PM Page 342

Page 360: Jabber Developer’s Handbook [Sams 2004]

343Jabber for Distributed Control

and it responded with my-jabber.Then the uname command was used to ask for theoperating system version information, which revealed that the remote system is a Linuxsystem. Finally, the w command was used to get a list of the current users and what pro-grams they’re running.

In addition to chatting (or messaging) with the systems one-on-one, you can alsoaddress them as a group.When the system clients start up, they join a chat room that wasalready set up for this purpose.The chat room serves as a multiplexer, enabling you tosend commands to all the computers at once and receive their replies in a single place.Figure 9.8 shows the Jabber browser view of the system control conference room.

Figure 9.8 Joining the system control conference room.

Figure 9.8 shows that there are already two systems in the conference room—one calledRemote Exec at AD and one called Remote Exec at my-jabber.These are the namesthat the system clients used when they joined the conference room. If you click on theJoin a Conference link in the left pane,WinJab brings up a chat window connected tothat conference room. Figure 9.9 shows what happens if you type a command in thatwindow.

Figure 9.9 Interacting with systems.

12 0672325365 CH09 1/21/05 2:25 PM Page 343

Page 361: Jabber Developer’s Handbook [Sams 2004]

344 Chapter 9 Jabber and System Control and Administration

The left pane shows the interaction with the systems.When user1 typed the hostnamecommand, it was sent to both computers and each responded with its hostname, prefixedby the nickname of the responding system.Then user1 sent the uname command, andeach host again replied. In this example, we’re using the Jabber conference component toforward the command to all listening hosts and also to collect their responses for us.

In the right pane, you can see that the conference room has four members now: thetwo systems and two users (user1 and user2). Because the Jabber conference compo-nent forwards all messages to all members, user2 has also seen user1’s commands andthe system responses, as shown in Figure 9.10.

Figure 9.10 Eavesdropping on system control.

Of course user2 could enter his own commands, which would be seen by user1.Thiswould be a great way for two system administrators who are geographically separated tocollaborate on a problem.

Now look at the Python script to implement the system control client.

Listing 9.3 System Control Script

1:import traceback

2:import string

3:import threading

4:import sys

5:import os

6:import jabber

7:import socket

8:

9:# terminate every command response with this

10:response_suffix = “COMMAND COMPLETE”

11:

12:# join this chatroom upon startup

13:system_chatroom = “[email protected]

14:

12 0672325365 CH09 1/21/05 2:25 PM Page 344

Page 362: Jabber Developer’s Handbook [Sams 2004]

345Jabber for Distributed Control

15:class ExecThread (threading.Thread) :

16: def __init__ (self, con, cmd, msg) :

17: threading.Thread.init(self)

18: self.cmd = cmd

19: self.msg = msg

20: self.con = con

21: def run (self):

22: try:

23: print “Executing: “+self.cmd

24: outStream = os.popen(self.cmd)

25: lines = outStream.readlines()

26: outStream.close()

27: print “Done with: “+self.cmd

28: body = string.join(lines) + response_suffix

29:

30: reply = self.msg.build_reply(reply_txt=body)

31: if self.msg.getType() == “groupchat”:

32: reply_to = self.msg.getFrom().getStripped()

33: reply.setTo(reply_to)

34: self.con.send(reply)

35:

36: except:

37: print traceback.print_exc(sys.exc_info())

38:

39:

40:def presenceCB(con, prs):

41: who = str(prs.getFrom())

42: type = prs.getType()

43: if type == None: type = ‘available’

44:

45: if type == ‘subscribe’:

46: con.send(jabber.Presence(to=who, type=’subscribed’))

47: con.send(jabber.Presence(to=who, type=’subscribe’))

48:

49: elif type == ‘unsubscribe’:

50: con.send(jabber.Presence(to=who, type=’unsubscribed’))

51: con.send(jabber.Presence(to=who, type=’unsubscribe’))

52:

53:

54:def disconnectedCB(con):

55: sys.exit(1)

56:

57:def messageCB(con, msg):

58: if msg.getBody().endswith(response_suffix) or \

Listing 9.3 Continued

12 0672325365 CH09 1/21/05 2:25 PM Page 345

Page 363: Jabber Developer’s Handbook [Sams 2004]

346 Chapter 9 Jabber and System Control and Administration

(msg.getFrom().getResource() == ‘’):

59: pass

60: else:

61: exec_thread = ExecThread(con, msg.getBody(), msg)

62: exec_thread.start()

63:

64:def startJabber(server, user, password, resource):

65: con = jabber.Client(host=server)

66: try:

67: con.connect()

68: con.setMessageHandler(messageCB)

69: con.setPresenceHandler(presenceCB)

70: con.setDisconnectHandler(disconnectedCB)

71:

72: if not con.auth(user, password, resource):

73: print “Jabber login failed: %s” % con.lastErr

74: sys.exit(1)

75:

76: print “Connected to Jabber”

77: con.requestRoster()

78: con.sendInitPresence()

79:

80: p = jabber.Presence(to=system_chatroom)

81: con.send(p)

82: iq = jabber.Iq(to=system_chatroom, type=”set”)

83: iq.setQuery(“jabber:iq:conference”)

84: iq.setQueryPayload(“<nick>Remote Exec at “+\

socket.gethostname()+”</nick>”)

85: response = con.SendAndWaitForResponse(iq)

86: data = response.getQueryNode()

87:

88: except:

89: print “Exception connecting to Jabber”

90: print traceback.print_exc(sys.exc_info())

91: sys.exit(1)

92: return con

93:

94:

95:try:

96: if __name__==’__main__’:

97:

98: if len(sys.argv) != 4:

99: print “usage: “,sys.argv[0], “ <server> <user> <password>”

100: sys.exit(1)

101:

Listing 9.3 Continued

12 0672325365 CH09 1/21/05 2:25 PM Page 346

Page 364: Jabber Developer’s Handbook [Sams 2004]

347Jabber for Distributed Control

102: server = sys.argv[1]

103: user = sys.argv[2]

104: password = sys.argv[3]

105:

106: resource = “exec”

107:

108: con = startJabber(server, user, password, resource)

109: while 1:

110: con.process(1)

111:except KeyboardInterrupt:

112: print traceback.print_exc(sys.exc_info())

113: sys.exit()

After importing the required Python modules, this script declares a couple of constantsthat it will use to manage the conference room interactions.

10:response_suffix = “COMMAND COMPLETE”

The client appends the response_suffix string to every message it sends and usesthe presence of this suffix to distinguish user command messages from system responsemessages.The conference component sends all messages to all members of the confer-ence room, so without this tag, the systems would have no way to distinguish user com-mands from system responses.

13:system_chatroom = “[email protected]

The system_chatroom is the JID of the conference room that the systems will alluse.This could just as easily be a program argument and read from sys.argv.

To enable the client to respond to more than one command at a time, we define aclass that can run as a separate thread and will be instantiated when a command messagearrives.This class is called ExecThread and is reprinted in the following lines:

15:class ExecThread (threading.Thread) :

16: def __init__ (self, cmd, msg) :

17: threading.Thread.init(self)

18: self.cmd = cmd

19: self.msg = msg

20:

21: def run (self):

22: try:

23: print “Executing: “+self.cmd

24: outStream = os.popen(self.cmd)

25: lines = outStream.readlines()

26: outStream.close()

27: print “Done with: “+self.cmd

28: body = string.join(lines) + response_suffix

29:

Listing 9.3 Continued

12 0672325365 CH09 1/21/05 2:25 PM Page 347

Page 365: Jabber Developer’s Handbook [Sams 2004]

348 Chapter 9 Jabber and System Control and Administration

30: reply = self.msg.build_reply(reply_txt=body)

31: if self.msg.getType() == “groupchat”:

32: reply_to = self.msg.getFrom().getStripped()

33: reply.setTo(reply_to)

34: con.send(reply)

35:

36: except:

37: print traceback.print_exc(sys.exc_info())

The arguments to the constructor (lines 16–19) are the command to be executed andthe message that generated the command.You need the message to generate a reply withthe appropriate thread and subject.The constructor saves these values for use in thethread’s main function run() (lines 21–37).

The run() function uses the Python popen() command to execute a new programwhile returning a stream containing that program’s text output. Lines 25 and 26 read allthe program output into the lines list variable. Line 28 joins those lines into a singlestring and appends the suffix that will tag this as a response and not a command. Line 30builds a reply message based on the command message with the body of the message setto the output of the command.

Lines 31–33 do a little magic to handle conference room (“groupchat”) messages.Each member of a conference room, when he joins the conference, is assigned a uniqueJID that is the JID of the conference room (room@server, for example, plus a randomresource (room@server/12345657, for example). Conference room members can use theJID containing the resource to send a message to an individual conference room mem-ber or omit the resource to send to everyone in the room.We want our responses to goto all members of the conference room, so we need to strip off the resource part of theto JID in the reply. Of course, this is necessary only if the command message’s type wasgroupchat;to reply directly to the sender you should leave the JID unchanged.

This example handles disconnection and presence just like the previous example, sowe won’t go into the details again.The next interesting aspect of this client is its messagehandling callback messageCB().

57:def messageCB(con, msg):

58: if msg.getBody().endswith(response_suffix) or \

(msg.getFrom().getResource() == ‘’):

59: pass

60: else:

61: exec_thread = ExecThread(msg.getBody(), msg)

62: exec_thread.start()

This method first checks to make sure that this is not a message from another systemcontrol client and that it’s not from a system service that has no resource.

NOTEActually, the system control client sees its own conference room messages in the messageCB()—not just

those of other clients.

12 0672325365 CH09 1/21/05 2:25 PM Page 348

Page 366: Jabber Developer’s Handbook [Sams 2004]

349Jabber for Distributed Control

If the message passes those checks, a new ExecThread object is created and started (lines61–62) to carry out the command.

When the system control client has connected to the Jabber server, it needs to jointhe system conference room.This is done with the <iq> and <presence> packets at lines80–86.

80: p = jabber.Presence(to=system_chatroom)

81: con.send(p)

82: iq = jabber.Iq(to=system_chatroom, type=”set”)

83: iq.setQuery(“jabber:iq:conference”)

84: iq.setQueryPayload(“<nick>Remote Exec at “+\

socket.gethostname()+”</nick>”)

85: response = con.SendAndWaitForResponse(iq)

86: data = response.getQueryNode()

Lines 80 and 81 send a presence packet to the conference room, announcing that weare online. Lines 82–85 actually register this client in the conference room.Those linessend a packet that looks like this:

<iq to=’[email protected]’ type=’set’ id=’2’>

<query xmlns = ‘jabber:iq:conference’ >

<nick>Remote Exec at AD</nick>

</query>

</iq>

Line 84 configures the <nick> element of the packet so that our “nickname” in theconference room includes the computer’s hostname, making it easier to distinguish thesystem responses. If all is successful, the conference room responds with a packet like this,containing the room name and the JID assigned to this client for this room:

<iq to=’syslog@my-jabber/exec’ type=’result’ id=’2’

from=’[email protected]’>

<query xmlns=’jabber:iq:conference’>

<nick>Remote Exec at AD</nick>

<name>System control group</name>

<id>[email protected]/3b795685ee55f6face2825df95fcf727</id>

</query>

</iq>

Next come some packets containing the list of the current members of the confer-ence room:

<iq type=’set’ to=’syslog@my-jabber/exec’ from=’[email protected]’>

<conference xmlns=’jabber:iq:browse’ name=’System control group’

type=’public’>

<user jid=’[email protected]/6dde6e870e99c39640a1312689d86c0e’

name=’Remote Exec at my-jabber’/>

<user jid=’[email protected]/17d1a3644b81a8475d5916972868a81b’

name=’user2’/>

<user jid=’[email protected]/27f8b164d7a04a98851bb91b31ea82e3’

12 0672325365 CH09 1/21/05 2:25 PM Page 349

Page 367: Jabber Developer’s Handbook [Sams 2004]

350 Chapter 9 Jabber and System Control and Administration

name=’user1’/>

</conference>

</iq>

<iq type=’set’ to=’syslog@my-jabber/exec’ from=’[email protected]’>

<user xmlns=’jabber:iq:browse’

jid=’[email protected]/3b795685ee55f6face2825df95fcf727’

name=’Remote Exec at AD’/>

</iq>

We’re not interested in the members of the conference room, so we just discard thisinformation.

NOTETo configure the conference room used in this example, add these lines to the <conference> section of

jabber.xml:

<room jid=”[email protected]”>

<name>System control group</name>

</room>

You may also find that you want to set the message history list to 0 so that new systems don’t get the last

few commands repeated when they come online. See Chapter 2, “Installing and Configuring Jabber

Software,” for details.

This example shows that with just a few lines of code, you can build a Jabber client thatcan be useful for lots of tasks.Although this one is general purpose, it could easily beadapted to other specific tasks.

Jabber for Application MonitoringThis section looks at the popular Apache Web server.Apache is an open source Webserver that is widely used for applications both big and small. Let’s apply the systemmonitoring concepts you saw above to the Apache server application.

The Apache server comes with a module that can serve up a Web page containing theserver’s status.This is good, but rather than remembering to check that page periodically,we’d like to build an agent that monitors that page and reports if there is a problem.

The Apache server is available from http://http.apache.org/. Download theinstallation package for your platform and follow the instructions on that site to install it.The Web server’s configuration is controlled through a configuration file calledhttp.conf, which is located in the conf/ directory of the Apache installation. It hasmany obscure options, but for now we’re interested in only the two options needed toturn on the server status page.

First, search through the http.conf file and find the line that looks like this:

#LoadModule status_module modules/mod_status.so

12 0672325365 CH09 1/21/05 2:25 PM Page 350

Page 368: Jabber Developer’s Handbook [Sams 2004]

351Jabber for Application Monitoring

This is the line that would load the status module, but it’s commented out. Removethe # to make the line look like this:

LoadModule status_module modules/mod_status.so

TIPIf you don’t find a LoadModule line like this, then the status module is probably compiled into your serv-

er. You can run httpd -l to see the list of compiled-in modules.

Enabling the mod_status module just gets basic status information; to get moredetails we need to enable the ExtendedStatus option. Search through http.conf for aline like this:

#ExtendedStatus On

Here again, this option is commented out. Remove the # at the beginning of the lineto get the following:

ExtendedStatus On

The last thing to do is to configure the /server-status path to point to the status module. Find the commented-out section of httpd.conf that looks like this:

#<Location /server-status>

# SetHandler server-status

# Order eny,allow

# Deny from all

# Allow from .example.com

#</Location>

Remove the comments so it looks like this:

<Location /server-status>

SetHandler server-status

Order eny,allow

Deny from all

Allow from .example.com

</Location>

Save httpd.conf, restart Apache, and the detailed stats should be available through the Web site at http://<servername>/server-status.This Web page is shown inFigure 5.11.

This is nicely formatted for human readability, but would be difficult to parse.Fortunately, another format is available, which you can get by modifying the URL tohttp://<servername>/server-status?auto.With the ?auto qualifier,Apache respondswith a simple text document that looks like this:

Total Accesses: 3

Total kBytes: 10

Uptime: 528

ReqPerSec: .00568182

12 0672325365 CH09 1/21/05 2:25 PM Page 351

Page 369: Jabber Developer’s Handbook [Sams 2004]

352 Chapter 9 Jabber and System Control and Administration

BytesPerSec: 19.3939

BytesPerReq: 3413.33

BusyWorkers: 1

IdleWorkers: 249

Scoreboard: ______________________________________________

Figure 9.11 Web server status page.

This is much easier to parse—just split the lines on the : characters.We’ll use this for-mat for the Jabber client.This client polls the status page and checks the values againstsome thresholds that we set up. If any of the values are out of range, it sends a messagecontaining the server status to every member of its roster who is online.

Figure 9.12 shows an example message from the Apache monitor.This message indi-cates that some of these values are out of their acceptable range.

TIPIf you really wanted to grab the administrators’ attention, you could make these messages be of type “head-

line.” Most IM clients will raise a new window when a headline message is received.

Additionally, if the service receives any regular message it responds to the sender withthe current stats. Figure 9.13 shows one of these messages.

This example uses a Java client to read the server’s status and generate the Jabber mes-sages.We’ll go through the code in detail, but first the full code is presented in Listing 9.4.

12 0672325365 CH09 1/21/05 2:25 PM Page 352

Page 370: Jabber Developer’s Handbook [Sams 2004]

353Jabber for Application Monitoring

Figure 9.12 Notification of abnormal Web server statistics.

Figure 9.13 Routine Web server statistics.

Listing 9.4 Apache Monitoring Client

1:package jdh.demos;

2:

3:import java.io.*;

4:import java.net.*;

5:import java.util.*;

6:

7:import org.jabber.jabberbeans.*;

8:import org.jabber.jabberbeans.Extension.IQAuthBuilder;

12 0672325365 CH09 1/21/05 2:25 PM Page 353

Page 371: Jabber Developer’s Handbook [Sams 2004]

354 Chapter 9 Jabber and System Control and Administration

9:import org.jabber.jabberbeans.util.*;

10:

11:public class ApacheMon implements PacketListener {

12:

13: public final int SLEEP_TIME = 60000;

14: private HashMap maxValues = new HashMap();

15: private HashMap lastData = new HashMap();

16:

17: private ConnectionBean cb;

18: private JID myJID;

19: private RosterBean rosterBean;

20: private PresenceBean presenceBean;

21: private String username;

22: private String password;

23: private String server;

24: private String resource;

25: private HashMap clientStatus = new HashMap();

26:

27: public ApacheMon(String[] args) {

28:

29: username = args[0];

30: password = args[1];

31: server = args[2];

32: resource = args[3];

33:

34: try {

35: startJabber();

36: } catch (Exception ex) {

37: ex.printStackTrace();

38: return;

39: }

40:

41: // These are the values that, when above the thresholds

42: // listed here, will cause this client to send a

43: // warning message to all online members of its roster.

44: maxValues.put(“ReqPerSec”, new Double(10.0));

45: maxValues.put(“BytesPerSec”, new Double(10000));

46: maxValues.put(“BytesPerReq”, new Double(10000));

47: maxValues.put(“BusyWorkers”, new Double(10));

48:

49: while (true) {

50: try {

51: HashMap data = getStatusData();

52: lastData = data;

53: if (checkData(data)) {

Listing 9.4 Continued

12 0672325365 CH09 1/21/05 2:25 PM Page 354

Page 372: Jabber Developer’s Handbook [Sams 2004]

355Jabber for Application Monitoring

54: sendData(data);

55: }

56: try {

57: Thread.sleep(SLEEP_TIME);

58: } catch (InterruptedException ex) {};

59: } catch (Exception ex) {

60: ex.printStackTrace();

61: }

62: }

63: }

64:

65: private boolean checkData(HashMap data) {

66: boolean ret = false;

67:

68: Iterator keys = maxValues.keySet().iterator();

69: while (keys.hasNext()) {

70: String key = (String) keys.next();

71: Double measured = (Double) data.get(key);

72: Double max = (Double) maxValues.get(key);

73: if ((measured != null) && (measured.compareTo(max) > 0.0)) {

74: ret = true;

75: break;

76: }

77: }

78:

79: return ret;

80: }

81:

82: private void sendData(HashMap data) {

83: String msgText = formatData(data);

84: Enumeration roster = rosterBean.entries();

85: while (roster.hasMoreElements()) {

86: RosterItem item = (RosterItem) roster.nextElement();

87: if (item.getSubscriptionType().equals(“none”)

88: || offline(item.getJID()))

89: continue;

90: MessageBuilder mb = new MessageBuilder();

91: mb.setToAddress(item.getJID());

92: mb.setFromAddress(myJID);

93: mb.setSubject(“Abnormal HTTP Server Stats”);

94: mb.setBody(msgText);

95:

96: try {

97: cb.send(mb.build());

98: } catch (InstantiationException e) {}

Listing 9.4 Continued

12 0672325365 CH09 1/21/05 2:25 PM Page 355

Page 373: Jabber Developer’s Handbook [Sams 2004]

356 Chapter 9 Jabber and System Control and Administration

99: }

100: }

101:

102: private boolean offline(JID jid) {

103: JID short_address = JID.fromString(jid.toSimpleString());

104: String presence = (String) clientStatus.get(short_address);

105: return (presence == null) || (“unavailable”.equals(presence));

106: }

107:

108: private String formatData(HashMap data) {

109: StringBuffer output = new StringBuffer(1024);

110: output.append(“Server measurements:\n”);

111: Iterator keys = data.keySet().iterator();

112: while (keys.hasNext()) {

113: String key = (String) keys.next();

114: output.append(key);

115: output.append(“ : “);

116: output.append(data.get(key));

117: output.append(“\n”);

118: }

119: return output.toString();

120: }

121:

122: private HashMap getStatusData()

123: throws IOException, MalformedURLException {

124: URL statusPage = new URL(“http://localhost:8080/server-status?auto”);

125: InputStream input = statusPage.openStream();

126:

127: BufferedReader reader =

128: new BufferedReader(new InputStreamReader(input));

129:

130: String line = null;

131: HashMap data = new HashMap();

132: for (line = reader.readLine();

133: line != null;

134: line = reader.readLine()) {

135:

136: try {

137: StringTokenizer toker = new StringTokenizer(line, “:”);

138: String key = toker.nextToken();

139: Double val = new Double(toker.nextToken());

140: data.put(key, val);

141: } catch (NumberFormatException nfe) {}

142: }

143: input.close();

Listing 9.4 Continued

12 0672325365 CH09 1/21/05 2:25 PM Page 356

Page 374: Jabber Developer’s Handbook [Sams 2004]

357Jabber for Application Monitoring

144: return data;

145: }

146:

147: private void startJabber() throws Exception {

148: cb = new ConnectionBean();

149: SyncPacketListener sync = new SyncPacketListener(cb);

150: Packet p;

151:

152: myJID = new JID(username, password, resource);

153:

154: cb.addPacketListener(new PacketDebug());

155: cb.connect(InetAddress.getByName(server));

156: InfoQueryBuilder iqb = new InfoQueryBuilder();

157: IQAuthBuilder auth = new IQAuthBuilder();

158: iqb.setType(“set”);

159: auth.setUsername(username);

160: auth.setPassword(password);

161: auth.setResource(resource);

162:

163: iqb.addExtension(auth.build());

164: Packet iq = iqb.build();

165: p = sync.sendAndWait((ContentPacket) iq, 5000);

166:

167: new MessengerBean(cb).addPacketListener(this);

168: rosterBean = new RosterBean();

169: rosterBean.setIQBean(new IQBean(cb));

170: rosterBean.refreshRoster();

171: presenceBean = new PresenceBean();

172: presenceBean.setConnBean(cb);

173: presenceBean.addPresenceListener(new OKPresenceListener());

174: PresenceBuilder pb = new PresenceBuilder();

175: pb.setStatus(“Available”);

176: presenceBean.getConnBean().send(pb.build());

177: }

178:

179: // Utility class to acknowledge presence subscribe/unsubscribe requests

180: private class OKPresenceListener implements PresenceListener {

181:

182: public void subscribe(Presence p) {

183: reply(p, “subscribed”);

184: reply(p, “subscribe”);

185: }

186: public void unsubscribe(Presence p) {

187: reply(p, “unsubscribed”);

188: reply(p, “unsubscribe”);

Listing 9.4 Continued

12 0672325365 CH09 1/21/05 2:25 PM Page 357

Page 375: Jabber Developer’s Handbook [Sams 2004]

358 Chapter 9 Jabber and System Control and Administration

189: }

190:

191: public void changedPresence(

192: Hashtable t,

193: Presence p,

194: PresenceUserNode n,

195: String s) {

196: JID jid = new JID(n.getName());

197: clientStatus.put(jid, s);

198: }

199: // Unneeded Jabberbeans PresenceListener methods

200: public void error(Presence p) {}

201: public void subscribed(Presence p) {}

202: public void unsubscribed(Presence p) {}

203:

204: private void reply(Presence p, String message) {

205: try {

206: PresenceBuilder pb = new PresenceBuilder();

207: pb.setFromAddress(p.getToAddress());

208: pb.setToAddress(p.getFromAddress());

209: pb.setType(message);

210: cb.send(pb.build());

211: } catch (InstantiationException e) {

212: e.printStackTrace();

213: }

214: }

215: }

216:

217: public void receivedPacket(PacketEvent pe) {

218: String msgText = formatData(lastData);

219: Message msg = (Message) pe.getPacket();

220: MessageBuilder mb = new MessageBuilder();

221: mb.setToAddress(msg.getFromAddress());

222: mb.setFromAddress(myJID);

223: mb.setSubject(“Server Status Report”);

224: mb.setBody(msgText);

225: mb.setThread(msg.getThread());

226: mb.setType(msg.getType());

227: try {

228: cb.send(mb.build());

229: } catch (InstantiationException e) {}

230:

231: }

232:

233: public void sendFailed(PacketEvent pe) {}

234: public void sentPacket(PacketEvent pe) {}

Listing 9.4 Continued

12 0672325365 CH09 1/21/05 2:25 PM Page 358

Page 376: Jabber Developer’s Handbook [Sams 2004]

359Jabber for Application Monitoring

235:

236: public static void main(String[] args) {

237: new ApacheMon(args);

238: }

239:}

The constructor (lines 27–63) for the ApacheMon class never returns—it parses thecommand-line arguments, starts Jabber, and then enters an infinite loop polling the Webserver status.The threshold values are stored in a hash map by their name for easy access.There is no significance to the values here; they’ll need to be tuned to the specific envi-ronment.

44: maxValues.put(“ReqPerSec”, new Double(10.0));

45: maxValues.put(“BytesPerSec”, new Double(10000));

46: maxValues.put(“BytesPerReq”, new Double(10000));

47: maxValues.put(“BusyWorkers”, new Double(10));

The server monitor will send alert messages when the number of requests per secondis greater than 10 (line 44), the number of bytes per second is greater than 10,000 (line45), the number of bytes per request is greater than 10,000 (line 46), or the number ofsimultaneous requests is greater than 10 (line 47).The main processing loop (reprintedbelow) just gets the statistics data from the Web server (line 51), stores it in case a statusrequest is received (line 52), checks the data against the thresholds (line 53), and, if anythreshold is exceeded, sends messages to the roster (line 54).Then it sleeps for a minuteand does it again.

49: while (true) {

50: try {

51: HashMap data = getStatusData();

52: lastData = data;

53: if (checkData(data)) {

54: sendData(data);

55: }

56: try {

57: Thread.sleep(SLEEP_TIME);

58: } catch (InterruptedException ex) {};

59: } catch (Exception ex) {

60: ex.printStackTrace();

61: }

62: }

This code doesn’t try to do anything to recover from exceptions—it just prints astack trace and tries again.

The initialization of the Jabber part of this client takes place in the startJabber()method (lines 147–177).After connecting and authenticating, we set up severalJabberBeans objects to manage presence, IQ, and message packets.

Listing 9.4 Continued

12 0672325365 CH09 1/21/05 2:25 PM Page 359

Page 377: Jabber Developer’s Handbook [Sams 2004]

360 Chapter 9 Jabber and System Control and Administration

167: new MessengerBean(cb).addPacketListener(this);

168: rosterBean = new RosterBean();

169: rosterBean.setIQBean(new IQBean(cb));

170: rosterBean.refreshRoster();

171: presenceBean = new PresenceBean();

172: presenceBean.setConnBean(cb);

173: presenceBean.addPresenceListener(new OKPresenceListener());

174: PresenceBuilder pb = new PresenceBuilder();

175: pb.setStatus(“Available”);

176: presenceBean.getConnBean().send(pb.build());

This client needs to receive message packets, so line 167 configures a messenger beanto deliver messages.We also need to maintain a roster so we can send alerts when theWeb server stats are above threshold. Lines 168–170 create a roster bean and ensure thatit is initialized from the server.To maintain that roster, we need to be able to handlepresence subscriptions with other clients and advertise our presence to them.The pres-ence bean created and initialized at lines 171–176 handles this for us.

After Jabber is initialized, we need to read the Web server status page.The Java URLclass (line 124 of the following lines) connects to the Web server status page and providesan input stream from which we can read. Notice the hard-coded URL of the server sta-tus page in line 124.

122: private HashMap getStatusData()

123: throws IOException, MalformedURLException {

124: URL statusPage = new URL(“http://localhost:8080/server-status?auto”);

125: InputStream input = statusPage.openStream();

126:

127: BufferedReader reader =

128: new BufferedReader(new InputStreamReader(input));

129:

130: String line = null;

131: HashMap data = new HashMap();

132: for (line = reader.readLine();

133: line != null;

134: line = reader.readLine()) {

135:

136: try {

137: StringTokenizer toker = new StringTokenizer(line, “:”);

138: String key = toker.nextToken();

139: Double val = new Double(toker.nextToken());

140: data.put(key, val);

141: } catch (NumberFormatException nfe) {}

142: }

143: input.close();

144: return data;

145: }

12 0672325365 CH09 1/21/05 2:25 PM Page 360

Page 378: Jabber Developer’s Handbook [Sams 2004]

361Jabber for Application Monitoring

We create a BufferedReader (line 127) so we can read the response line by line ratherthan as a stream of bytes and use a StringTokenizer (lines 137–139) to split the line tothe left and right of the colon character.The first token on the line becomes the key tofind the data value in the hash map. So if the server returns a document like this:

Total Accesses: 137

Total kBytes: 279809

Uptime: 7603

ReqPerSec: .0180192

BytesPerSec: 37685.7

BytesPerReq: 2091420

BusyWorkers: 2

IdleWorkers: 248

It will get stored in the hash map as these key-value pairs:

[“Total Accesses”, 137]

[“Total kBytes”, 279809]

[“Uptime”, 7603]

[“ReqPerSec”, .0180192]

[“BytesPerSec”, 37685.7]

[“BytesPerReq”, 2091420]

[“BusyWorkers”, 2]

[“IdleWorkers”, 248]

This makes it easy to compare this data to the threshold data, which is stored in thesame format.The checkData() method (lines 65–77) does just that. It loops through thethreshold values and compares each one to the corresponding value in the measureddata. If any of the measured data exceed the threshold, the sendData() method sendsthe data set to all online members of the roster.

82: private void sendData(HashMap data) {

83: String msgText = formatData(data);

84: Enumeration roster = rosterBean.entries();

85: while (roster.hasMoreElements()) {

86: RosterItem item = (RosterItem) roster.nextElement();

87: if (item.getSubscriptionType().equals(“none”)

88: || offline(item.getJID()))

89: continue;

90: MessageBuilder mb = new MessageBuilder();

91: mb.setToAddress(item.getJID());

92: mb.setFromAddress(myJID);

93: mb.setSubject(“Abnormal HTTP Server Stats”);

94: mb.setBody(msgText);

95:

96: try {

97: cb.send(mb.build());

98: } catch (InstantiationException e) {}

99: }

100: }

12 0672325365 CH09 1/21/05 2:25 PM Page 361

Page 379: Jabber Developer’s Handbook [Sams 2004]

362 Chapter 9 Jabber and System Control and Administration

After formatting the data into a text message (line 83), this method loops through themembers of the roster, and for each one, checks to make sure they are

n still subscribed (line 87) andn not offline (line 88)

In the JabberBeans roster implementation, when a client unsubscribes to presence, itsentry is not removed from the roster, its type is set to none, and the entry remains in thelist—hence the checks at line 87 and 88.This is not very intuitive, but it mirrors the waythe server stores the information in XDB.

To simplify handling the online/offline status of roster members, this client keeps ahash map that maps JIDs to presence strings.The changedPresence() method of thepresence listener class is called when one of the roster members changes presence status.

191: public void changedPresence(

192: Hashtable t,

193: Presence p,

194: PresenceUserNode n,

195: String s) {

196: JID jid = new JID(n.getName());

197: clientStatus.put(jid, s);

198: }

This method just extracts the client’s name from the JabberBeans PresenceUserNodeobject and uses it to index the clientStatus hash map.This name doesn’t include the resource, so as long as the client is online at any resource, it will show up in this hash map.

These methods handle the polling of the Web server, roster and presence handling,analysis of the data, and the sending of alerts.The only thing left is for the code torespond to Jabber messages.The client responds to these messages with the last measure-ment data. Recall that the main processing loop in the constructor saved a reference tothe last measurements in the lastData field.The receivedData() method useslastData to respond to client messages.

217: public void receivedPacket(PacketEvent pe) {

218: String msgText = formatData(lastData);

219: Message msg = (Message) pe.getPacket();

220: MessageBuilder mb = new MessageBuilder();

221: mb.setToAddress(msg.getFromAddress());

222: mb.setFromAddress(myJID);

223: mb.setSubject(“Server Status Report”);

224: mb.setBody(msgText);

225: mb.setThread(msg.getThread());

226: mb.setType(msg.getType());

227: try {

228: cb.send(mb.build());

229: } catch (InstantiationException e) {}

12 0672325365 CH09 1/21/05 2:25 PM Page 362

Page 380: Jabber Developer’s Handbook [Sams 2004]

363Summary

Here we use the same method to format the message data as we did earlier (line 218),but use a different message subject to distinguish it from an alert (line 223). It’s impor-tant to copy the thread ID and type fields (line 225–226) so the response to a chat mes-sage appears in the same chat window at the client.

Now you can deploy this little service with the Web server and it will let you know ifthere is a problem. No more manual scanning of statistics data! Of course this was a verysimple example, but the technique applies to any system or application that has an inter-face amenable to being read by another program. Network management systems thatoffer an SNMP interface or even just an HTTP interface could be wrapped by a Jabberclient, which could monitor their status and alert their operators if there is a problem.

SummaryIn this chapter you saw an example of a Jabber client that monitored system eventsunder Windows or Unix and forwarded those events to Jabber subscribers.You then sawsome ways to use the Jabber protocol to monitor the version and time information ofdistributed components.The Jabber conference room service was used to build a clientthat could control and monitor multiple computers at once. Finally, a client was builtthat monitored the Apache Web server’s status and reported statistics to administrators ondemand or when they exceeded a threshold.

Managing computer systems has never been easy, and the complexity of modern sys-tems has only added to the difficulty.We hope that we’ve shown how the simple butpowerful Jabber protocols and services can help ease the pain by providing a straightfor-ward and flexible common interface to disparate distributed systems.

12 0672325365 CH09 1/21/05 2:25 PM Page 363

Page 381: Jabber Developer’s Handbook [Sams 2004]

12 0672325365 CH09 1/21/05 2:25 PM Page 364

Page 382: Jabber Developer’s Handbook [Sams 2004]

10Jabber and JXTA

Alone we can do so little; together we can do so much.

Helen Keller

THIS CHAPTER INTRODUCES JXTA, A RELATIVELY new technology for building peer-to-peer applications, and compares it to Jabber in terms of its protocols, implementations,and design philosophy. It includes a detailed examination of an example Jabber clientthat bridges between these two technologies and highlights in code how to use them together.

JXTA Technology IntroductionJXTA (short for juxtapose) was introduced by Sun Microsystems in 2001 as a new opensource development project to create an infrastructure that developers could use to buildvery large-scale dynamic networks of peers.The motivation for JXTA was very similar tothe motivation for Jabber: Several different, incompatible protocols for P2P computingwere emerging (Napster, Gnutella, and so on), but their lack of interoperability limitedtheir usefulness. So, although Jabber is intended to unify the instant messaging protocolspace, JXTA is aimed more broadly at unifying the protocols used by P2P applications ingeneral.

Another key design goal of the JXTA protocols is that they be platform independent,so there are no restrictions on the programming languages that can be used to imple-ment the protocols.This platform independence extends to data transport mechanisms—there can be no requirement for a particular network transport protocol (TCP/IP, forinstance). Finally, there can be no dependence on a specific operating system or hardwareconfiguration. Like Jabber, JXTA uses a set of XML messages to achieve this platform

13 0672325365 CH10 1/21/05 2:26 PM Page 365

Page 383: Jabber Developer’s Handbook [Sams 2004]

366 Chapter 10 Jabber and JXTA

independence. Unlike Jabber, as of this writing the only mature and complete open-source implementation of the protocols is in Java, which is the language we will use inthe examples that follow.

The JXTA protocols are intended to be implemented on all scales of computers fromcell phones to PCs to large servers. So (again like Jabber) the JXTA specifications do notrequire every peer to implement the full protocol suite—peers need to implement onlythose protocol elements that they require to do their jobs.This implies that the networkof peers be heterogeneous and that each peer must be able to accommodate the exis-tence of peers that speak and understand a subset of the protocol.They must also accom-modate messages from peers that speak a different subset of the protocol.

With JXTA, resources can be dynamically discovered by peers and utilized withoutrequiring explicit configuration.This is a powerful tool for building dynamic, large-scaleapplications because maintaining accurate configuration information is difficult if notimpossible after applications reach enormous size. If components can make themselvesand their capabilities automatically known, the configuration information is more likelycorrect.

Elements of JXTA TechnologyAs you can see, the elements of JXTA technology have parallels in Jabber in many ways,but they differ in some important aspects.This section examines the pieces that make upJXTA.

IdentifiersJXTA uses unique identifiers in the form of uniform resource names (URNs), which serveas unique and unambiguous references to JXTA entities such as a peer or peer group.These identifiers are typically randomly generated as needed by JXTA implementations,although there are several “well-known” identifiers for common services. Here is anexample of a JXTA ID for a peer:

urn:jxta:uuid-59616261646162614A78746150325033F3BC76FF13C2414CBC0AB663666DA53903

Obviously, these are not intended to be human-readable.

AdvertisementsAdvertisements are XML documents that describe a resource such as a peer or a service.They are used to describe many types of JXTA resources, and applications are free to addtheir own advertisement types.Advertisements are the means by which JXTA peers dis-cover other entities and get information about how to contact other peers, join peergroups, and invoke services. Listing 10.1 provides an example of a peer advertisement.

13 0672325365 CH10 1/21/05 2:26 PM Page 366

Page 384: Jabber Developer’s Handbook [Sams 2004]

367Elements of JXTA Technology

Listing 10.1 A Peer Advertisement

<?xml version=”1.0”?>

<!DOCTYPE jxta:PA>

<jxta:PA xmlns:jxta=”http://jxta.org”>

<PID>

urn:jxta:uuid-59616261646162614A78746150325033FDDDE9C5656440AC8F…

</PID>

<GID>

urn:jxta:jxta-NetGroup

</GID>

<Name>

JabberPeer

</Name>

<Svc>

<MCID>

urn:jxta:uuid-DEADBEEFDEAFBABAFEEDBABE0000000805

</MCID>

<Parm>

<jxta:RA xmlns:jxta=”http://jxta.org”>

<Dst>

<jxta:APA xmlns:jxta=”http://jxta.org”>

<EA>

tcp://192.168.1.3:9703

</EA>

<EA>

http://192.168.1.3:9702

</EA>

</jxta:APA>

</Dst>

</jxta:RA>

</Parm>

</Svc>

<Svc>

<MCID>

urn:jxta:uuid-DEADBEEFDEAFBABAFEEDBABE0000000105

</MCID>

<Parm>

<RootCert>

MIICPDCCAaWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBmMRUwEwYDVQQKEwx3d3cu

anh0YS5vcmcxCzAJBgNVBAcTAlNGMQswCQYDVQQGEwJVUzEUMBIGA1UEAxMLdXNl

cm5hbWUtQ0ExHTAbBgNVBAsTFDExMTcxRDVGMUJBRUJFNENCNDk5MB4XDTAyMTIz

MTE2MTA1NFoXDTEyMTIzMTE2MTA1NFowZjEVMBMGA1UEChMMd3d3Lmp4dGEub3Jn

MQswCQYDVQQHEwJTRjELMAkGA1UEBhMCVVMxFDASBgNVBAMTC3VzZXJuYW1lLUNB

MR0wGwYDVQQLExQxMTE3MUQ1RjFCQUVCRTRDQjQ5OTCBmzALBgkqhkiG9w0BAQED

13 0672325365 CH10 1/21/05 2:26 PM Page 367

Page 385: Jabber Developer’s Handbook [Sams 2004]

368 Chapter 10 Jabber and JXTA

gYsAMIGHAoGBALol+TS9f/5zgOOmkpXLweI+BUfyzXIWoBfAPx8lL8AzL9zy88eA

urLPogd2a2G3IjuCGtBsVRK7j9l5MxTJm6gCig/mxvyufllmYnm4ofmfi6N5xX8e

3I0v/knbHmknbmppGQnzcHMmMdvvewhG/+tcnwRoiI512O6WnC6v+XGBAgERMA0G

CSqGSIb3DQEBBQUAA4GBAJ8DpaVuyskVny2oXBRGT6eXcHpJu+yM6XJ14637Dwub

aS6+HhAV4P35wzpJENsah850/xVGfxfFOj8E+6+0YgBap3KyvfT/RH98LDHx4Fok

y2VzTn99J3XLL9UENKpPi7dY/TMG8ASBaAshjl2fnQ9y6h6ewkpzx9o++BiJhFh4

</RootCert>

</Parm>

</Svc>

</jxta:PA>

The details here are not important, but there are a couple of things to notice.The adver-tisement contains four types of elements within the top-level <jxta:PA> (PA stands forpeer advertisement) element: <PID> is the unique identifier for this peer; <GID> is theunique identifier for the group in which this advertisement is valid; <Name> is thehuman-readable name of this peer; and each <Svc> element defines a service offered bythis peer. Each of the services has its own set of parameters, including things such ascontact URLs and cryptographic certificates.

MessagesMessages are the data that are exchanged between peers and consist of an ordered list ofname-value pairs called message elements.The values in these elements are arbitrary butare often things such as advertisements and identifiers. JXTA messages are analogous toJabber packets in that they are the lowest-level data elements exchanged between peers.They differ from Jabber packets in that they are complete XML documents, not docu-ment fragments. Connections between JXTA peers are not maintained between messagesas they are between Jabber clients and servers, so making each message an XML docu-ment makes it easier to parse.

PeersA peer is any entity that implements one or more of the JXTA protocols and is unique-ly identified by its peer identifier. Peers advertise some number of endpoints that they useto communicate with one another over whatever transport protocols they have available.(The URLs in Listing 10.1 are peer endpoints.) Peers can communicate even if theydon’t have a direct point-to-point network path between them by using other peers toroute their messages.This is similar to how Jabber servers route messages, with theexceptions that a Jabber server cannot discover other servers and a Jabber server routespackets directly to the destination server—it cannot use a number of intermediaryservers to get a packet to its final destination.

Listing 10.1 Continued

13 0672325365 CH10 1/21/05 2:26 PM Page 368

Page 386: Jabber Developer’s Handbook [Sams 2004]

369Elements of JXTA Technology

Peer GroupsA set of peers that agree on a common set of services can form a peer group in whichthose services are used.All peers initially belong to a global peer group called the NetPeer Group, which uses standard JXTA services. But peers can create and join any num-ber of other groups as well. Peer groups can require certain credentials or authenticationfrom peers that wish to join the group, or they can allow all peers access to the group.Because services are bound to peer groups, they provide a means for controlling thescope of advertisement and discovery queries, enabling JXTA networks to scale to largenumbers of peers.

JXTA peer groups have no equivalent construct in Jabber. Jabber networks performthis scoping by counting on the locality of reference between clients on a Jabber server.That is, most messages sent by a client will be destined for the server to which it’s con-nected, or to a client connected to the same server.This is true in most cases, givingJabber a scalability advantage over an architecture that requires all peers to talk directly toall others.

PipesJXTA pipes are the virtual channels over which JXTA peers send messages. By default,they are uni-directional and unreliable, meaning that messages may be dropped.However, pipe implementations exist that build on this protocol to provide bi-directionaland reliable pipes.The core pipe protocol also provides a type of pipe called a propagatepipe, which is a broadcast pipe in which a message sent to the pipe can be delivered tomany recipients.

In general, peers in a JXTA application communicate with other peers by discoveringpipe advertisements and sending messages on those pipes.The example application thatappears later in this chapter shows communication over JXTA pipes.

Modules (Services)Every peer group provides a set of services provided by modules, which can be discov-ered and dynamically invoked. Core services that are not implemented by a peer groupare provided by the Net Peer Group. Briefly, the important core services are

n Discovery Service. Used by peers to search within a peer group for resourcessuch as other peers, peer groups, and pipes.

n Membership Service. This service determines whether a new peer will beallowed to join the peer group and provides the mechanisms for peers to join andleave the group.

n Pipe Service. The pipe service creates and manages pipe connections.n Resolver Service. This service is used to send arbitrary query requests to other

peers.These queries can be defined to include any application-specific data.

13 0672325365 CH10 1/21/05 2:26 PM Page 369

Page 387: Jabber Developer’s Handbook [Sams 2004]

370 Chapter 10 Jabber and JXTA

Roles for JXTA PeersDepending on the services they provide, JXTA peers can perform different roles in theJXTA network.These roles enable JXTA peers to provide some of the capabilities pro-vided by the Jabber server in mediating communication between edge peers.Any partic-ular peer may expose services that implement any of these roles or none of them.A peerthat implements none of these roles is a simple edge peer.

Rendezvous PeersRendezvous peers provide a service for delivering broadcast messages to other peers.These broadcast messages are used primarily for discovery, but are also used for propaga-tion pipes.

NOTEApple Computer’s OS X operating system includes a technology called Rendezvous that addresses a similar

problem as JXTA Rendezvous, but is a different technology.

Relay PeersA relay peer can route messages between peers that otherwise could not communicatebecause of a firewall or NAT (network address translation) barrier.

Proxy PeersA proxy peer acts on behalf of another peer that may not be capable of maintaining its own presence as a network device. Proxy peers are commonly used for resource-constrained devices such as mobile phones, and intermittently-connected devices such asPDAs. Proxy peers have a store-and-forward capability to accommodate devices that areonline only for short periods.

Trying Out JXTAThe JXTA Web site at http://www.jxta.org contains lots of useful JXTA informationand links to the download site for the JXTA code at http://download.jxta.org.Thereare several platform bindings for different programming languages, including the Javaimplementation that we’ll use for the examples to follow.

The easiest way to get started is to download the Easy Installer for your operating sys-tem.The Easy Installers are built from the latest released code and support most commonoperating systems. Download the distribution that’s appropriate for your environment andinstall it according to the instructions.

13 0672325365 CH10 1/21/05 2:26 PM Page 370

Page 388: Jabber Developer’s Handbook [Sams 2004]

371Trying Out JXTA

The Easy Installer comes with a couple of example applications that you can use to get familiar with the technology. Here, we’ll focus on the JXTA shell, which is a command-driven application that can be used to interactively demonstrate some of theJXTA features. Run the JXTA shell (through the Windows “Start” menu or from theinstallation directory on Unix), and its configuration screen is displayed as shown inFigure 10.1.

Figure 10.1 The JXTA basic configuration window.

Figure 10.1 shows that the name of this peer is “shell” and that it won’t use a proxyserver.You’ll need to use a proxy server only if you are behind a firewall and want tocommunicate with a peer outside the firewall.

In addition to the peer name, you also have to configure a username and password inthe Security tab as shown in Figure 10.2.This username and password will be requiredevery time the shell starts up.

TIPYou can avoid having to type in the username and password every time by adding the Java properties

–Dnet.jxta.tls.principal=<username> and –Dnet.jxta.tls.password=

<password> to the Java command line.

Click the OK button to dismiss the Configurator.The next window is the main JXTAshell command window, as shown in Figure 10.3.

13 0672325365 CH10 1/21/05 2:26 PM Page 371

Page 389: Jabber Developer’s Handbook [Sams 2004]

372 Chapter 10 Jabber and JXTA

Figure 10.2 The JXTA security configuration window.

Figure 10.3 The JXTA Shell window.

At the bottom, the JXTA> prompt is ready for a command.The commands to the JXTAshell are modeled on the Unix shell commands, so to get a list of the available com-mands, type man as shown in Listing 10.2.

13 0672325365 CH10 1/21/05 2:26 PM Page 372

Page 390: Jabber Developer’s Handbook [Sams 2004]

373Trying Out JXTA

Listing 10.2 A Sample JXTA Shell Command

JXTA>man

The ‘man’ command is the primary manual system for the JXTA Shell.

The usage of man is:

JXTA> man <commandName>

For instance typing

JXTA> man Shell

displays man page about the Shell

The following is the list of commands available:

cat Concatanate and display a Shell object

chpgrp Change the current peer group

clear Clear the shell’s screen

env Display environment variable

exit Exit the Shell

exportfile Export to an external file

flush flush a jxta advertisement

get Get data from a pipe message

grep Search for matching patterns

groups Discover peer groups

help No description available for this ShellApp

history No description available for this ShellApp

importfile Import an external file

instjar Installs jar-files containing additional Shell commands

join Join a peer group

kdb run the httpd

leave Leave a peer group

login login

man An on-line help command that displays information about a

mkadv Make an advertisement

mkmsg Make a pipe message

mkpgrp Create a new peer group

mkpipe Create a pipe

more Page through a Shell object

peerconfig Peer Configuration

peerinfo Get information about peers

peers Discover peers

publish publish a jxta advertisement

put Put data into a pipe message

rdv

rdvserver No description available for this ShellApp

rdvstatus Display information about rendezvous

13 0672325365 CH10 1/21/05 2:26 PM Page 373

Page 391: Jabber Developer’s Handbook [Sams 2004]

374 Chapter 10 Jabber and JXTA

recv Receive a message from a pipe

relaystatus Display information about existing relay

route Display information about existing route

rsh No description available for this ShellApp

rshd Remote JXTA Shell Deamon

search Discover jxta advertisements

send Send a message into a pipe

set Set an environment variable

setenv Set an environment variable

sftp Send a file to another peer

share Share an advertisement

Shell JXTA Shell command interpreter

sql Issue an SQL command (not implemented)

sqlshell JXTA SQL Shell command interpreter

talk Talk to another peer

transports Display information about the message transports in

➥ the current group

uninstjar Uninstalls jar-files previously installed with ‘instjar’

version No description available for this ShellApp

wc Count the number of lines, words, and chars in an object

who Display credential information

whoami Display information about a peer or peergroup

xfer Send a file to another peer

JXTA>

The peers command is used to discover and show the list of known peers.When usedwith the –r argument, it sends a discovery request message; when used without argu-ment, it prints the list of already-discovered peers.When the shell first starts, it knowsabout only the local peer, but it can find out about other peers by performing a remotediscovery request.This is shown in the following shell session:

JXTA>peers

peer0: name = shell

JXTA>peers -r

peer discovery message sent

JXTA>peers

peer0: name = shell

peer1: name = otherPeer

JXTA>

The first peers command shows only the shell itself.The last peers command showsthat another peer (named otherPeer) has been discovered. Of course, this is all happen-ing asynchronously, so more peers may show up in the list at any time.

Listing 10.2 Continued

13 0672325365 CH10 1/21/05 2:26 PM Page 374

Page 392: Jabber Developer’s Handbook [Sams 2004]

375The JXTA Java Binding API

One simple JXTA application supported by the JXTA shell is a primitive chat func-tion called talk. Using the talk function to send a message requires three steps:

1. Create a pipe advertisement that others can discover and use to send messages tous.The talk –register command creates this advertisement.

2. Start a listener thread to watch for messages on that pipe.The talk –login com-mand starts this thread.

3. Use the talk –u command to send the message.

To set up a peer to receive a message, you have to follow the first two steps by typingthese commands at the shell prompt:

JXTA>talk -register recipient

.....................

User : recipient is now registered

JXTA>talk -login recipient

JXTA>

The peer that is to send the message needs to follow all three steps as follows:

JXTA>talk -register sender

.....................

User : sender is now registered

JXTA>talk -login sender

JXTA>talk -u sender recipient

.found user’s advertisement attempting to connect

talk is connected to user recipient

Type your message. To exit, type “.” at begining of line

Hello there, recipient.

.

JXTA>

The first two talk commands establish the local pipe advertisement and start the pipelistener was shown in the recipient’s example.The third talk command is of the follow-ing format:

talk –u <sender_name> <receiver_name>

There is a slight pause while the shell discovers the recipient’s pipe advertisement.After it’s found, the user is prompted for the messages and they are sent on the pipe onemessage per line.We’ll see how this works in more detail in the example application laterin this chapter.

The JXTA Java Binding APIAs we mentioned, the JXTA protocols are not tied to any particular programming lan-guage, but the most robust implementation is in Java. Covering the entire JXTA Java APIis well outside the scope of this book, but a quick overview will help with the exampleto follow.

13 0672325365 CH10 1/21/05 2:26 PM Page 375

Page 393: Jabber Developer’s Handbook [Sams 2004]

376 Chapter 10 Jabber and JXTA

All JXTA services are offered with respect to a particular peer group, so thePeerGroup class serves as an important handle to access services. PeerGroup has methodssuch as getDiscoveryService() and getPipeService() to retrieve the discovery andpipe services for this group.The default peer group, to which all peers belong, is theNetPeerGroup.The PeerGroupFactory class has a static method newNetPeerGroup()that returns a reference to the default group.

As you would imagine, the DiscoveryService is used to discover advertisements forthings such as peers, groups, and pipes. It maintains a local cache of advertisements, so ithas two methods of access. getLocalAdvertisements() returns a set of advertisementsmatching a query specification. getRemoteAdvertisements() sends a discovery requestmessage to neighboring JXTA peers.

The PipeService is responsible for creating InputPipe and OutputPipe objects.Because the default JXTA pipes are unidirectional, we’ll be creating both types to carryout two-way communications.

You’d be right to notice that advertisements are an important part of the JXTA pro-tocols.They are created using a static method called AdvertisementFactory.newAdvertisement().This method takes the advertisement type as a parameter andreturns an advertisement instance ready for its fields to be filled in.

The last JXTA class that we’ll mention here is the Message class.As we mentioned,JXTA messages are made up of message elements, so building a message consists ofbuilding a sequence of message elements.You’ll see an example in the next section.

Example: A Jabber-to-JXTA BridgeIn this section, we present an example of a simple integration of Jabber with JXTA.Thisexample is a Jabber client that can accept presence subscriptions and forward Jabber mes-sages to JXTA shell peers by using the simple talk protocol you saw earlier. It is also aJXTA peer that can receive talk protocol JXTA messages and forward them to allJabber clients on its roster. Just for fun, we’ll have it also interpret some Jabber messagesas commands to report on the JXTA status:

n discover—Causes the client to try to discover peer and group advertisements.Any advertisements that are discovered are sent in Jabber messages to everyone onthe roster.

n peers—Causes the client to respond to the sender with a list of the currentlyknown peers.

n groups—Causes the client to respond to the sender with a list of the currentlyknown peer groups.

Before you dive into the code, it’s a good idea to see how it works.The place to start iswith a JXTA shell and the JXTA-Jabber client.Then we use a WinJab client to chat withthe JXTA-Jabber client about the JXTA peers, groups, and advertisements.We also addthe JXTA-Jabber client to the roster (and by extension, add ourselves to its roster).

13 0672325365 CH10 1/21/05 2:26 PM Page 376

Page 394: Jabber Developer’s Handbook [Sams 2004]

377Example: A Jabber-to-JXTA Bridge

Figure 10.4 shows a chat session in which we ask the JXTA-Jabber client what peers andgroups it sees, and then ask it to discover peers and groups.

Figure 10.4 Chatting with the JXTA-Jabber client.

The last chat command, discover, causes the JXTA-Jabber client to send a remote dis-covery request.As other peers respond to its request, the client sends Jabber messages toall clients on its roster containing the discovered advertisement.They show up as Jabbermessages, as shown in Figure 10.5.

Figure 10.5 Advertisements discovered by the JXTA-Jabber client.

CAUTIONIf a lot of JXTA advertisements are discovered, the JXTA-Jabber client can generate a lot of messages in a

short period. The Jabber server may stop servicing messages from the JXTA-Jabber client if its karma value

gets too low. See Chapter 2, “Installing and Configuring Jabber Software,” for a discussion of karma and

how to configure it.

13 0672325365 CH10 1/21/05 2:26 PM Page 377

Page 395: Jabber Developer’s Handbook [Sams 2004]

378 Chapter 10 Jabber and JXTA

To send a JXTA talk message from the JXTA shell to the Jabber clients, we use thethree variants of the talk command as before:

JXTA>talk -register shell

.....................

User : shell is now registered

JXTA>talk -login shell

JXTA>talk -u shell JxtaToJabber

.found user’s advertisement attempting to connect

talk is connected to user JxtaToJabber

Type your message. To exit, type “.” at begining of line

Hello from JXTA!

.

JXTA>

The message “Hello from JXTA!” is sent to all clients on the JXTA-Jabber client’sroster.They are delivered in WinJab as shown in figure 10.6.

Figure 10.6 A message relayed from JXTA to Jabber.

To go the other way, from Jabber to JXTA, we send a Jabber message to the JXTA-Jabber client and set the subject to the name of the JXTA talker who should get themessage.The next figure shows the message in WinJab just before it’s sent.

This message is sent to the JXTA talker’s shell and looks like this:

JXTA>talk: from JxtaToJabber to shell

Message: Hello from Jabber!

NOTERemember, the JXTA-Jabber client may have to discover the recipient’s pipe advertisement, so there could be

a delay before the message gets delivered to the JXTA shell. After the first message to a recipient, other

messages will go quickly because the pipe advertisement is cached.

13 0672325365 CH10 1/21/05 2:26 PM Page 378

Page 396: Jabber Developer’s Handbook [Sams 2004]

379Example: A Jabber-to-JXTA Bridge

Figure 10.7 A message relayed from Jabber to JXTA.

Now that you’ve seen what it does, you can look at how this all works.

Details of the talk ProtocolIn the preceding discussion of the shell, we alluded to the fact that the communicationbetween the talkers happens on a JXTA pipe.To create and discover these pipes, we needto understand the format of the pipe advertisement used by the talk protocol. Here’s anexample of a talk pipe advertisement:

1:<?xml version=”1.0”?>

2:<!DOCTYPE jxta:PipeAdvertisement>

3:<jxta:PipeAdvertisement xmlns:jxta=”http://jxta.org”>

4: <Id>

5: urn:jxta:uuid-59616261646162614E5047205032503387C17C5F0C7B479DB15B…

6: </Id>

7: <Type>

8: JxtaUnicast

9: </Type>

10: <Name>

11: JxtaTalkUserName.recipient

12: </Name>

13:</jxta:PipeAdvertisement>

The important pieces of this advertisement aren Line 3.The root element, jxta:PipeAdvertisement, identifies this XML docu-

ment as a pipe advertisement.n Line 5.This is the unique identifier that distinguishes this pipe advertisement from

all other pipe advertisements.n Line 8.The <Type> of this pipe is JxtaUnicast, which means that messages on

this pipe flow from one peer to one other peer.Another type of pipe isJxtaPropagate, which enables one peer to send messages to many other peers.

13 0672325365 CH10 1/21/05 2:26 PM Page 379

Page 397: Jabber Developer’s Handbook [Sams 2004]

380 Chapter 10 Jabber and JXTA

n Line 11.As far as the JXTA protocols are concerned, the <Name> element is arbi-trary.The shell talk command uses the name to encode its own information:The name of the talker listening on this pipe follows the period. In this case it is“recipient.”

This example is complicated by the asynchronous nature of the JXTA protocols.When aJabber message arrives, we may not have yet discovered the PipeAdvertisement for therecipient, so in some cases we have to start a discovery request and keep track of theJabber messages that are waiting to be sent.Then when (actually, if) the recipient’s pipeadvertisement is discovered, we can send the queued message.We’ll point out the queueas we go through the example.

As is our wont, we’ll present the complete listing here in Listing 10.3 and then exam-ine it in sections.

Listing 10.3 The JXTA-Jabber Client

1:package jdh.demos;

2:import java.io.*;

3:import java.net.InetAddress;

4:import java.util.*;

5:import net.jxta.discovery.*;

6:import net.jxta.document.*;

7:import net.jxta.endpoint.InputStreamMessageElement;

8:import net.jxta.endpoint.MessageElement;

9:import net.jxta.id.IDFactory;

10:import net.jxta.peergroup.*;

11:import net.jxta.pipe.*;

12:import net.jxta.protocol.*;

13:import org.jabber.jabberbeans.*;

14:import org.jabber.jabberbeans.Extension.IQAuthBuilder;

15:import org.jabber.jabberbeans.util.*;

16:public class JxtaClient

17: implements DiscoveryListener, PacketListener, PipeMsgListener {

18: private ConnectionBean cb;

19: private String username, password, resource, server_host;

20: private RosterBean rosterBean;

21: private JID myJID;

22: private PeerGroup rootGroup;

23: private Vector msgQueue = new Vector();

24: public JxtaClient(String[] args) {

25: username = args[0];

13 0672325365 CH10 1/21/05 2:26 PM Page 380

Page 398: Jabber Developer’s Handbook [Sams 2004]

381Example: A Jabber-to-JXTA Bridge

26: password = args[1];

27: server_host = args[2];

28: resource = args[3];

29: try {

30: startJabber();

31: startJxta();

32: } catch (Exception ex) {

33: ex.printStackTrace();

34: }

35: }

36: private void startJxta() throws Exception {

37: rootGroup = PeerGroupFactory.newNetPeerGroup();

38: DiscoveryService disco = rootGroup.getDiscoveryService();

39: disco.addDiscoveryListener(this);

40: disco.flushAdvertisements(null, DiscoveryService.GROUP);

41: disco.flushAdvertisements(null, DiscoveryService.PEER);

42: disco.flushAdvertisements(null, DiscoveryService.ADV);

43: PipeAdvertisement pipeadv;

44: Enumeration pipeAds =

45: disco.getLocalAdvertisements(

46: DiscoveryService.ADV,

47: “name”,

48: “JxtaTalkUserName.” + username);

49: if (pipeAds.hasMoreElements()) {

50: pipeadv = (PipeAdvertisement) pipeAds.nextElement();

51: } else {

52: pipeadv =

53: (PipeAdvertisement) AdvertisementFactory.newAdvertisement(

54: PipeAdvertisement.getAdvertisementType());

55: PeerGroupID pgid = rootGroup.getPeerGroupID();

56: pipeadv.setPipeID(IDFactory.newPipeID(pgid));

57: pipeadv.setName(“JxtaTalkUserName.” + username);

58: pipeadv.setType(PipeService.UnicastType);

59: }

60: InputPipe inputPipe =

61: rootGroup.getPipeService().createInputPipe(pipeadv, this);

62: disco.publish(pipeadv, DiscoveryService.ADV);

63: disco.remotePublish(pipeadv, DiscoveryService.ADV);

64: }

65: private void startJabber() throws Exception {

66: cb = new ConnectionBean();

Listing 10.3 Continued

13 0672325365 CH10 1/21/05 2:26 PM Page 381

Page 399: Jabber Developer’s Handbook [Sams 2004]

382 Chapter 10 Jabber and JXTA

67: SyncPacketListener sync = new SyncPacketListener(cb);

68: Packet p;

69: myJID = new JID(username, password, resource);

70: cb.addPacketListener(new PacketDebug());

71: cb.connect(InetAddress.getByName(server_host));

72: InfoQueryBuilder iqb = new InfoQueryBuilder();

73: IQAuthBuilder auth = new IQAuthBuilder();

74: iqb.setType(“set”);

75: auth.setUsername(username);

76: auth.setPassword(password);

77: auth.setResource(resource);

78: iqb.addExtension(auth.build());

79: Packet iq = iqb.build();

80: p = sync.sendAndWait((ContentPacket) iq, 5000);

81: new MessengerBean(cb).addPacketListener(this);

82: rosterBean = new RosterBean();

83: rosterBean.setIQBean(new IQBean(cb));

84: rosterBean.refreshRoster();

85: PresenceBean presenceBean = new PresenceBean();

86: presenceBean.setConnBean(cb);

87: presenceBean.addPresenceListener(new OKPresenceListener());

88: PresenceBuilder pb = new PresenceBuilder();

89: pb.setStatus(“Available”);

90: presenceBean.getConnBean().send(pb.build());

91: }

92: public void receivedPacket(PacketEvent pe) {

93: Message jabberMsg = (Message) pe.getPacket();

94: try {

95: if ((jabberMsg.getSubject() == null)

96: || jabberMsg.getSubject().equals(“”)) {

97: // This is a ‘discover’ ‘peers’ or ‘groups’ message

98: String command = jabberMsg.getBody().toLowerCase();

99: if (command.startsWith(“discover”)) {

100: System.out.println(“Discovering....”);

101: rootGroup.getDiscoveryService().getRemoteAdvertisements(

102: null,

103: DiscoveryService.GROUP,

104: null,

105: null,

106: 10);

107: rootGroup.getDiscoveryService().getRemoteAdvertisements(

108: null,

Listing 10.3 Continued

13 0672325365 CH10 1/21/05 2:26 PM Page 382

Page 400: Jabber Developer’s Handbook [Sams 2004]

383Example: A Jabber-to-JXTA Bridge

109: DiscoveryService.PEER,

110: null,

111: null,

112: 10);

113: } else if (command.startsWith(“peers”)) {

114: System.out.println(“listing peers....”);

115: Enumeration ads =

116: rootGroup.getDiscoveryService().getLocalAdvertisements(

117: DiscoveryService.PEER,

118: null,

119: null);

120: StringBuffer response = new StringBuffer(1024);

121: response.append(“Known Peers are: “);

122: while (ads.hasMoreElements()) {

123: PeerAdvertisement peerAd =

124: (PeerAdvertisement) ads.nextElement();

125: response.append(peerAd.getName());

126: if (ads.hasMoreElements())

127: response.append(“, “);

128: }

129: MessageBuilder mb = new MessageBuilder();

130: mb.setBody(response.toString());

131: mb.setSubject(jabberMsg.getSubject());

132: mb.setThread(jabberMsg.getThread());

133: mb.setFromAddress(jabberMsg.getToAddress());

134: mb.setType(jabberMsg.getType());

135: mb.setToAddress(jabberMsg.getFromAddress());

136: cb.send(mb.build());

137: } else if (command.startsWith(“groups”)) {

138: System.out.println(“listing groups....”);

139: Enumeration ads =

140: rootGroup.getDiscoveryService().getLocalAdvertisements(

141: DiscoveryService.GROUP,

142: null,

143: null);

144: StringBuffer response = new StringBuffer(1024);

145: response.append(“Known Groups are: “);

146: while (ads.hasMoreElements()) {

147: PeerGroupAdvertisement peerGroupAd =

148: (PeerGroupAdvertisement) ads.nextElement();

149: response.append(peerGroupAd.getName());

150: if (ads.hasMoreElements())

151: response.append(“, “);

152: }

153: MessageBuilder mb = new MessageBuilder();

154: mb.setBody(response.toString());

Listing 10.3 Continued

13 0672325365 CH10 1/21/05 2:26 PM Page 383

Page 401: Jabber Developer’s Handbook [Sams 2004]

384 Chapter 10 Jabber and JXTA

155: mb.setSubject(jabberMsg.getSubject());

156: mb.setThread(jabberMsg.getThread());

157: mb.setFromAddress(jabberMsg.getToAddress());

158: mb.setType(jabberMsg.getType());

159: mb.setToAddress(jabberMsg.getFromAddress());

160: cb.send(mb.build());

161: }

162: } else {

163: /* Assume it’s a message for a JXTA peer,

164: * Subject contains the dest peer name */

165: Enumeration ads =

166: rootGroup.getDiscoveryService().getLocalAdvertisements(

167: DiscoveryService.ADV,

168: “Name”,

169: “JxtaTalkUserName.” + jabberMsg.getSubject());

170: if (ads.hasMoreElements()) {

171: PipeAdvertisement ad = (PipeAdvertisement) ads.nextElement();

172: sendToPipe(ad, jabberMsg);

173: } else { // can’t do it now. Queue it.

174: synchronized (this) {

175: msgQueue.add(jabberMsg);

176: }

177: rootGroup.getDiscoveryService().getRemoteAdvertisements(

178: null,

179: DiscoveryService.ADV,

180: “Name”,

181: “JxtaTalkUserName.” + jabberMsg.getSubject(),

182: 10);

183: }

184: }

185: } catch (Exception e) {}

186: }

187: public void discoveryEvent(DiscoveryEvent event) {

188: String type = “UNKNOWN”;

189: switch (event.getResponse().getDiscoveryType()) {

190: case DiscoveryService.ADV :

191: type = “ADV”;

192: // We may have discovered a talk pipe advert.

193: // Send any messages that are waiting.

194: checkPendingMessages(event.getSearchResults());

195: break;

196: case DiscoveryService.GROUP :

197: type = “GROUP”;

198: break;

199: case DiscoveryService.PEER :

Listing 10.3 Continued

13 0672325365 CH10 1/21/05 2:26 PM Page 384

Page 402: Jabber Developer’s Handbook [Sams 2004]

385Example: A Jabber-to-JXTA Bridge

200: type = “PEER”;

201: break;

202: }

203: Enumeration results = event.getSearchResults();

204: while (results.hasMoreElements()) {

205: Advertisement adv = (Advertisement) results.nextElement();

206: sendToAll(type + “ Advertisement Discovered”, adv.toString());

207: }

208: }

209: private void sendToAll(String subject, String msgText) {

210: Enumeration roster = rosterBean.entries();

211: while (roster.hasMoreElements()) {

212: RosterItem item = (RosterItem) roster.nextElement();

213: MessageBuilder mb = new MessageBuilder();

214: mb.setToAddress(item.getJID());

215: mb.setFromAddress(myJID);

216: mb.setSubject(subject);

217: mb.setBody(msgText);

218: try {

219: cb.send(mb.build());

220: } catch (InstantiationException e) {}

221: }

222: }

223: private synchronized void checkPendingMessages(Enumeration ads) {

224: if (msgQueue.isEmpty())

225: return;

226: Vector sentMessages = new Vector();

227: while (ads.hasMoreElements()) {

228: Advertisement ad = (Advertisement) ads.nextElement();

229: if (ad instanceof PipeAdvertisement) {

230: PipeAdvertisement newPipeAd = (PipeAdvertisement) ad;

231: String name = newPipeAd.getName();

232: Enumeration waitingMessages = msgQueue.elements();

233: while (waitingMessages.hasMoreElements()) {

234: Message jabberMsg = (Message) waitingMessages.nextElement();

235: if (name.equals(“JxtaTalkUserName.” + jabberMsg.getSubject())) {

236: sendToPipe(newPipeAd, jabberMsg);

237: sentMessages.add(jabberMsg);

238: }

239: }

240: }

241: }

242: msgQueue.removeAll(sentMessages);

243: }

Listing 10.3 Continued

13 0672325365 CH10 1/21/05 2:26 PM Page 385

Page 403: Jabber Developer’s Handbook [Sams 2004]

386 Chapter 10 Jabber and JXTA

244: public void pipeMsgEvent(PipeMsgEvent event) {

245: net.jxta.endpoint.Message msg = event.getMessage();

246: MessageElement nameElement =

247: msg.getMessageElement(“JxtaTalkSenderName”);

248: MessageElement msgElement =

249: msg.getMessageElement(“JxtaTalkSenderMessage”);

250: String subject =

251: “Jxta Talk Message From “ + new String(nameElement.getBytes(false));

252: String message = new String(msgElement.getBytes(false));

253: sendToAll(subject, message);

254: }

255: private void sendToPipe(PipeAdvertisement pipeAd, Message jabberMsg) {

256: try {

257: OutputPipe outputPipe =

258: rootGroup.getPipeService().createOutputPipe(pipeAd, 1000);

259: net.jxta.endpoint.Message jxtaMsg = new net.jxta.endpoint.Message();

260: MessageElement messageElement = null;

261: messageElement =

262: new InputStreamMessageElement(

263: “JxtaTalkSenderName”,

264: new MimeMediaType(“text/plain”),

265: new ByteArrayInputStream(username.getBytes()),

266: null);

267: jxtaMsg.replaceMessageElement(messageElement);

268: messageElement =

269: new InputStreamMessageElement(

270: “JxtaTalkSenderMessage”,

271: new MimeMediaType(“text/plain”),

272: new ByteArrayInputStream(jabberMsg.getBody().getBytes()),

273: null);

274: jxtaMsg.replaceMessageElement(messageElement);

275: outputPipe.send(jxtaMsg);

276: outputPipe.close();

277: } catch (IOException e) {

278: e.printStackTrace();

279: }

280: }

281: // Unneeded JabberBeans PacketListener methods

282: public void sendFailed(PacketEvent pe) {}

283: public void sentPacket(PacketEvent pe) {}

Listing 10.3 Continued

13 0672325365 CH10 1/21/05 2:26 PM Page 386

Page 404: Jabber Developer’s Handbook [Sams 2004]

387Example: A Jabber-to-JXTA Bridge

284: // Utility class to acknowledge presence subscribe/unsubscribe requests

285: private class OKPresenceListener implements PresenceListener {

286: public void subscribe(Presence p) {

287: reply(p, “subscribed”);

288: }

289: public void unsubscribe(Presence p) {

290: reply(p, “unsubscribed”);

291: }

292: // Unneeded JabberBeans PresenceListener methods

293: public void changedPresence(

294: Hashtable t,

295: Presence p,

296: PresenceUserNode n,

297: String s) {

298: }

299: public void error(Presence p) {}

300: public void subscribed(Presence p) {}

301: public void unsubscribed(Presence p) {}

302: private void reply(Presence p, String message) {

303: try {

304: PresenceBuilder pb = new PresenceBuilder();

305: pb.setFromAddress(p.getToAddress());

306: pb.setToAddress(p.getFromAddress());

307: pb.setType(message);

308: cb.send(pb.build());

309: } catch (InstantiationException e) {

310: e.printStackTrace();

311: }

312: }

313: }

314: public static void main(String[] args) {

315: new JxtaClient(args);

316: }

317:}

Starting at the end of the listing, line 314 is the main() method, which just instantiatesan instance of the JxtaClient class. Its constructor is at lines 24–35.The constructorparses the Jabber username, password, server host, and resource from the command lineand uses them to connect to the Jabber server at line 30 (the startJabber() method).At line 31, the call to the startJxta() method initializes the JXTA libraries.

Listing 10.4 looks at the startJxta() method in some detail.

Listing 10.3 Continued

13 0672325365 CH10 1/21/05 2:26 PM Page 387

Page 405: Jabber Developer’s Handbook [Sams 2004]

388 Chapter 10 Jabber and JXTA

Listing 10.4 Initializing the JXTA Libraries

36: private void startJxta() throws Exception {

37: rootGroup = PeerGroupFactory.newNetPeerGroup();

38: DiscoveryService disco = rootGroup.getDiscoveryService();

39: disco.addDiscoveryListener(this);

40: disco.flushAdvertisements(null, DiscoveryService.GROUP);

41: disco.flushAdvertisements(null, DiscoveryService.PEER);

42: disco.flushAdvertisements(null, DiscoveryService.ADV);

43: PipeAdvertisement pipeadv;

44: Enumeration pipeAds =

45: disco.getLocalAdvertisements(

46: DiscoveryService.ADV,

47: “name”,

48: “JxtaTalkUserName.” + username);

49: if (pipeAds.hasMoreElements()) {

50: pipeadv = (PipeAdvertisement) pipeAds.nextElement();

51: } else {

52: pipeadv =

53: (PipeAdvertisement) AdvertisementFactory.newAdvertisement(

54: PipeAdvertisement.getAdvertisementType());

55: PeerGroupID pgid = rootGroup.getPeerGroupID();

56: pipeadv.setPipeID(IDFactory.newPipeID(pgid));

57: pipeadv.setName(“JxtaTalkUserName.” + username);

58: pipeadv.setType(PipeService.UnicastType);

59: }

60: InputPipe inputPipe =

61: rootGroup.getPipeService().createInputPipe(pipeadv, this);

62: disco.publish(pipeadv, DiscoveryService.ADV);

63: disco.remotePublish(pipeadv, DiscoveryService.ADV);

64: }

All JXTA services are defined in the context of a particular peer group, so the first thingwe need to do is get a reference to the Net Peer Group—the default peer group towhich all peers belong.The call to PeerGroupFactory.newNetPeerGroup() at line 37returns that reference. Using that peer group, we can get its DiscoveryService (line 38)so we can discover peers, peer groups, and advertisements in the Net Peer Group.Thecall to addDiscoveryListener() at line 39 registers this object to be called when adver-tisements are discovered.We implement the JXTA DiscoveryListener interface and itsmethod discoveryEvent() (lines 187–207) to receive notification of discovered adver-tisements. Of course, we won’t discover anything until we call the DiscoveryServicemethod getRemoteAdvertisements() to send a discovery request to other peers. Lines40–42 clear the cache of any stored advertisements from any previous times this clientwas run.

13 0672325365 CH10 1/21/05 2:26 PM Page 388

Page 406: Jabber Developer’s Handbook [Sams 2004]

389Example: A Jabber-to-JXTA Bridge

The next thing we need to do is create the pipe that other peers can use to send usJXTA talk messages. Lines 43–59 create the advertisement for the talk pipe.At lines44–50, we look in the local cache to see whether it contains an appropriate pipe advertise-ment from a previous run by using the DiscoveryService getLocalAdvertisements()

method.The parameters to getLocalAdvertisements() determine what advertisementwe’re looking for. DiscoveryService.ADV (line 46) tells the discovery service that we’relooking for a generic advertisement—not a peer or peer group. Line 47 (“name”) and line48 (“JxtaTalkUserName.” + username) tell the discovery service that we’re looking for an advertisement in which the <name> element matches the concatenation of“JxtaTalkUserName.” and our Jabber username.The getLocalAdvertisements() APIaccepts simple wildcarding (using “*”), but we want an exact match in this case. If the dis-covery service finds a matching advertisement in the cache, we’ll reuse it (line 50).Otherwise, we create a new PipeAdvertisement (lines 52–58) and initialize its name tothe concatenation of “JxtaTalkUserName.” and our Jabber username. Notice line 56,which creates a new unique identifier for the new pipe advertisement.

CAUTIONThe attentive reader may be asking, “If you cleared the advertisement cache (line 42), why bother looking

for a cached pipe advertisement (line 45)?” Good question. Some early versions of the Java JXTA platform

have a bug that can cause flushAdvertisements() to not work when used like this. To really be

sure the cache is flushed, you need to remove the files that JXTA uses to store cached data. They are in the

JXTA_HOME directory (usually the directory in which you ran the program) in a directory called cm. This

directory can be safely deleted before starting the JXTA program.

Flushing the advertisements like this can cause problems for other JXTA clients that may have cached the

PipeAdvertisement from a previous run of JxtaClient. They may end up with two different pipe

advertisements (remember, the unique ID determines the ad’s identity) with the same value for the <name>

element. If so, you’d need to clear their caches, too.

All that’s left to get the JXTA initialization completed is to open the pipe to receivemessages using this statement (line 61):

rootGroup.getPipeService().createInputPipe(pipeadv, this);

This statement creates a JXTA input pipe that uses pipeadv as its advertisement andregisters this as a pipe listener.When a message is received on this pipe, JXTA calls thepipeMsgEvent() method of the PipeMsgListener interface (lines 243–253). Finally, noone will know about this pipe unless we advertise it by using the discovery servicemethods publish() to publish it locally and remotePublish() to send it to remotepeers.

Starting the Jabber client is straightforward.The startJabber() method (repeated in Listing 10.5) authenticates to the Jabber server, fetches our roster, and sends our presence.

13 0672325365 CH10 1/21/05 2:26 PM Page 389

Page 407: Jabber Developer’s Handbook [Sams 2004]

390 Chapter 10 Jabber and JXTA

Listing 10.5 Initializing the Jabber Client

65: private void startJabber() throws Exception {

66: cb = new ConnectionBean();

67: SyncPacketListener sync = new SyncPacketListener(cb);

68: Packet p;

69: myJID = new JID(username, password, resource);

70: cb.addPacketListener(new PacketDebug());

71: cb.connect(InetAddress.getByName(server_host));

72: InfoQueryBuilder iqb = new InfoQueryBuilder();

73: IQAuthBuilder auth = new IQAuthBuilder();

74: iqb.setType(“set”);

75: auth.setUsername(username);

76: auth.setPassword(password);

77: auth.setResource(resource);

78: iqb.addExtension(auth.build());

79: Packet iq = iqb.build();

80: p = sync.sendAndWait((ContentPacket) iq, 5000);

81: new MessengerBean(cb).addPacketListener(this);

82: rosterBean = new RosterBean();

83: rosterBean.setIQBean(new IQBean(cb));

84: rosterBean.refreshRoster();

85: PresenceBean presenceBean = new PresenceBean();

86: presenceBean.setConnBean(cb);

87: presenceBean.addPresenceListener(new OKPresenceListener());

88: PresenceBuilder pb = new PresenceBuilder();

89: pb.setStatus(“Available”);

90: presenceBean.getConnBean().send(pb.build());

91: }

Lines 69–79 build the authentication packet to log us in to the Jabber server. In line 80,the sendAndWait() method is a handy way to send a packet and wait for a response.Weassume that the Jabber server’s response is a success packet and that it’s okay to proceed.Because we’re interested in only Jabber message packets (and not IQ or presence pack-ets), we add ourselves as a packet listener by using a MessengerBean at line 81.We keeptrack of the roster by using a RosterBean (lines 82–84) and use a PresenceBean (lines85–90) to handle our presence status and subscriptions.

At this point, Jabber and JXTA are both initialized and we’re just waiting for a mes-sage on either of them.When we receive a Jabber message, the receivedPacket()method (lines 92–186) is called. In the real world, this method would probably be refac-tored because it does several different things. If the message has no subject line, it inter-prets the body of the message as a command. If the body starts with discover we start

13 0672325365 CH10 1/21/05 2:26 PM Page 390

Page 408: Jabber Developer’s Handbook [Sams 2004]

391Example: A Jabber-to-JXTA Bridge

remote discovery requests for groups and peers by using getRemoteAdvertisements().This example starts a discovery request for peer group advertisements.

101: rootGroup.getDiscoveryService().getRemoteAdvertisements(

102: null,

103: DiscoveryService.GROUP,

104: null,

105: null,

106: 10);

The null value at line 102 means to send this discovery request to all peers withinthe group.The null values in lines 104 and 105 mean that we’re interested in seeing allgroups.These parameters could be used to filter the results in the same way that you sawin the call to getLocalAdvertisements() at line 45.

If the body of the Jabber message starts with peers, we respond with a message con-taining the names of all of the peers that we have discovered (see Listing 10.6).

Listing 10.6 Enumerating the Known Peers

115: Enumeration ads =

116: rootGroup.getDiscoveryService().getLocalAdvertisements(

117: DiscoveryService.PEER,

118: null,

119: null);

120: StringBuffer response = new StringBuffer(1024);

121: response.append(“Known Peers are: “);

122: while (ads.hasMoreElements()) {

123: PeerAdvertisement peerAd =

124: (PeerAdvertisement) ads.nextElement();

125: response.append(peerAd.getName());

126: if (ads.hasMoreElements())

127: response.append(“, “);

128: }

129: MessageBuilder mb = new MessageBuilder();

130: mb.setBody(response.toString());

131: mb.setSubject(jabberMsg.getSubject());

132: mb.setThread(jabberMsg.getThread());

133: mb.setFromAddress(jabberMsg.getToAddress());

134: mb.setType(jabberMsg.getType());

135: mb.setToAddress(jabberMsg.getFromAddress());

136: cb.send(mb.build());

Here we use the discovery service getLocalAdvertisements() to search the cache forpeer advertisements (line 116).We loop through each advertisement and append its name(line 125) to our response. Finally, we build a Jabber message in response and send it(lines 129–136). Notice that because the thread and type values have been copied from

13 0672325365 CH10 1/21/05 2:26 PM Page 391

Page 409: Jabber Developer’s Handbook [Sams 2004]

392 Chapter 10 Jabber and JXTA

the source message into the response (lines 132 and 134), if the incoming message is achat message, the response will appear in the chat window rather than as a separate message.

If the body of the incoming Jabber message starts with groups, we respond with amessage containing the names of all of the peer groups that we have discovered.Thiscode is almost exactly the same as Listing 10.6, so we won’t repeat it here.

The last section of the receivedPacket() method (lines 162–184) interpret the sub-ject line of the incoming Jabber message as the name of a JXTA talk user and the bodyas the message that we should send to that user.

162: } else {

163: /* Assume it’s a message for a JXTA peer,

164: * Subject contains the dest peer name */

165: Enumeration ads =

166: rootGroup.getDiscoveryService().getLocalAdvertisements(

167: DiscoveryService.ADV,

168: “Name”,

169: “JxtaTalkUserName.” + jabberMsg.getSubject());

170: if (ads.hasMoreElements()) {

171: PipeAdvertisement ad = (PipeAdvertisement) ads.nextElement();

172: sendToPipe(ad, jabberMsg);

173: } else { // can’t do it now. Queue it.

174: synchronized (this) {

175: msgQueue.add(jabberMsg);

176: }

177: rootGroup.getDiscoveryService().getRemoteAdvertisements(

178: null,

179: DiscoveryService.ADV,

180: “Name”,

181: “JxtaTalkUserName.” + jabberMsg.getSubject(),

182: 10);

183: }

184: }

First, we look in the local cache for an advertisement that matches the name in thesubject of the Jabber message (lines 165–171). If we find the pipe advertisement, we canjust send the message immediately (line 172). If we don’t find the pipe advertisement, weneed to store the message in a message queue (line 175) and start a discovery request totry to find the pipe advertisement (lines 177–182).

If an advertisement is discovered, the JXTA platform calls the discoveryEvent()method (lines 187–207 in Listing 10.7).This could happen either because we received aJabber message with the body discover or because there are pending messages waitingto be sent.

13 0672325365 CH10 1/21/05 2:26 PM Page 392

Page 410: Jabber Developer’s Handbook [Sams 2004]

393Example: A Jabber-to-JXTA Bridge

Listing 10.7 A JXTA Advertisement Is Discovered

187: public void discoveryEvent(DiscoveryEvent event) {

188: String type = “UNKNOWN”;

189: switch (event.getResponse().getDiscoveryType()) {

190: case DiscoveryService.ADV :

191: type = “ADV”;

192: // We may have discovered a talk pipe advert.

193: // Send any messages that are waiting.

194: checkPendingMessages(event.getSearchResults());

195: break;

196: case DiscoveryService.GROUP :

197: type = “GROUP”;

198: break;

199: case DiscoveryService.PEER :

200: type = “PEER”;

201: break;

202: }

203: Enumeration results = event.getSearchResults();

204: while (results.hasMoreElements()) {

205: Advertisement adv = (Advertisement) results.nextElement();

206: sendToAll(type + “ Advertisement Discovered”, adv.toString());

207: }

208: }

If the discovered advertisements are of type DiscoveryService.ADV, they might be a pipe advertisement for a message that we’re waiting to send. Line 194 callscheckPendingMessages() (lines 223–243) with the results of the JXTA discovery to seewhether there are any waiting messages. In any event, we send each discovered advertise-ment to every client on our roster (lines 203–207) by using the sendToAll() methodshown in Listing 10.8.

Listing 10.8 Sending a Message to Everyone on the Roster

209: private void sendToAll(String subject, String msgText) {

210: Enumeration roster = rosterBean.entries();

211: while (roster.hasMoreElements()) {

212: RosterItem item = (RosterItem) roster.nextElement();

213: MessageBuilder mb = new MessageBuilder();

214: mb.setToAddress(item.getJID());

215: mb.setFromAddress(myJID);

216: mb.setSubject(subject);

217: mb.setBody(msgText);

218: try {

219: cb.send(mb.build());

220: } catch (InstantiationException e) {}

221: }

222: }

13 0672325365 CH10 1/21/05 2:26 PM Page 393

Page 411: Jabber Developer’s Handbook [Sams 2004]

394 Chapter 10 Jabber and JXTA

This method uses the RosterBean to get the list of subscribed clients (line 210) andsends each one a message with the subject and body passed in as parameters.

The other thing we have to do when we discover a JXTA advertisement is lookthrough the unsent messages to see whether the advertisement is for a talk pipe forwhich we have queued messages.The checkPendingMessages() method (repeated inListing 10.9) does this.

Listing 10.9 Sending Queued Message to JXTA

223: private synchronized void checkPendingMessages(Enumeration ads) {

224: if (msgQueue.isEmpty())

225: return;

226: Vector sentMessages = new Vector();

227: while (ads.hasMoreElements()) {

228: Advertisement ad = (Advertisement) ads.nextElement();

229: if (ad instanceof PipeAdvertisement) {

230: PipeAdvertisement newPipeAd = (PipeAdvertisement) ad;

231: String name = newPipeAd.getName();

232: Enumeration waitingMessages = msgQueue.elements();

233: while (waitingMessages.hasMoreElements()) {

234: Message jabberMsg = (Message) waitingMessages.nextElement();

235: if (name.equals(“JxtaTalkUserName.” + jabberMsg.getSubject())) {

236: sendToPipe(newPipeAd, jabberMsg);

237: sentMessages.add(jabberMsg);

238: }

239: }

240: }

241: }

242: msgQueue.removeAll(sentMessages);

243: }

Because we have stored all of the pending messages in the msgQueue vector, we need toloop through all the discovered advertisements and, for each one, loop through all thepending messages.There are certainly more efficient ways to do this, but this is easy tofollow.The only tricky thing is we don’t want to delete elements from the vector whilewe’re iterating through its elements, so we make a temporary vector (sentMessages) tohold the messages that we send and should remove from msgQueue.We delete them allafter we’re finished sending messages (line 242).

The method in which the JXTA messages are actually sent is called sendToPipe()(lines 255–280 in Listing 10.10).

13 0672325365 CH10 1/21/05 2:26 PM Page 394

Page 412: Jabber Developer’s Handbook [Sams 2004]

395Example: A Jabber-to-JXTA Bridge

Listing 10.10 Sending a JXTA Message

255: private void sendToPipe(PipeAdvertisement pipeAd, Message jabberMsg) {

256: try {

257: OutputPipe outputPipe =

258: rootGroup.getPipeService().createOutputPipe(pipeAd, 1000);

259: net.jxta.endpoint.Message jxtaMsg = new net.jxta.endpoint.Message();

260: MessageElement messageElement = null;

261: messageElement =

262: new InputStreamMessageElement(

263: “JxtaTalkSenderName”,

264: new MimeMediaType(“text/plain”),

265: new ByteArrayInputStream(username.getBytes()),

266: null);

267: jxtaMsg.replaceMessageElement(messageElement);

268: messageElement =

269: new InputStreamMessageElement(

270: “JxtaTalkSenderMessage”,

271: new MimeMediaType(“text/plain”),

272: new ByteArrayInputStream(jabberMsg.getBody().getBytes()),

273: null);

274: jxtaMsg.replaceMessageElement(messageElement);

275: outputPipe.send(jxtaMsg);

276: outputPipe.close();

277: } catch (IOException e) {

278: e.printStackTrace();

279: }

280: }s

Given a pipe advertisement, we need to create an output pipe before we can send a mes-sage. Line 256 uses the pipe service to create the pipe.The second parameter, 1000, tellsthe pipe service to try for one second (1000 milliseconds) to create the pipe. It’s possiblethat it could take longer to establish the pipe, so a robust application would handle thatpossibility by blocking indefinitely (by using –1 as the timeout) or setting up an outputpipe listener.

NOTEBoth the JabberBeans library and the JXTA library include classes named Message, so we have to use the

fully-qualified classname for the JXTA Message class in line 259 so the Java compiler knows which one

we mean.

13 0672325365 CH10 1/21/05 2:26 PM Page 395

Page 413: Jabber Developer’s Handbook [Sams 2004]

396 Chapter 10 Jabber and JXTA

A JXTA message is made up of a sequence of message elements and the JXTA shell talkprotocol calls for two elements.The first element has the name JxtaTalkSenderName andcontains the name of the talker sending the message.The other element has the name“JxtaTalkSenderMessage” and, unsurprisingly, contains the text of the message.The JavaJXTA API has many ways to create message elements; lines 261–266 show one of thesimplest. It creates a message element with the name “JxtaTalkSenderName”. Everymessage element has a MIME type that declares what type of data is contained within it.This element is just plain text, so we use the MIME type “text/plain”.This type ofmessage element takes its contents from an input stream, so (line 265) we create aByteArrayInputStream by using our Jabber username as the data source.The final nullat line 266 indicates that this element has no associated digital signature element. Line267 adds the new message element to the message, replacing any other element with thesame name.

The other message element is constructed in the same manner, and the whole mes-sage is sent to the output pipe at line 275. Now JXTA will deliver the message to theremote shell application.

Handling a received JXTA message requires us to pull out the JXTA message ele-ments and send Jabber messages.The pipeMsgEvent method is called when data arriveson the input pipe, as shown in Listing 10.11.

Listing 10.11 Receiving a JXTA Message

244: public void pipeMsgEvent(PipeMsgEvent event) {

245: net.jxta.endpoint.Message msg = event.getMessage();

246: MessageElement nameElement =

247: msg.getMessageElement(“JxtaTalkSenderName”);

248: MessageElement msgElement =

249: msg.getMessageElement(“JxtaTalkSenderMessage”);

250: String subject =

251: “Jxta Talk Message From “ + new String(nameElement.getBytes(false));

252: String message = new String(msgElement.getBytes(false));

253: sendToAll(subject, message);

254: }

Lines 246 and 248 show how to extract the talker’s name and message text, respectively.We build a subject line for the Jabber message by using the name of the JXTA sender(line 250) and extract the text of the JXTA talk message for the body of the Jabbermessage (line 252).The sendToAll() method is the same one you saw in Listing 10.7—it sends the message to every client on the roster.

So that’s it.This was one of our longer examples, but it shows many of the detailsinvolved in translating between the two technologies.As you can see, it would be possi-ble without too much trouble to turn this example into a Jabber gateway service.

13 0672325365 CH10 1/21/05 2:26 PM Page 396

Page 414: Jabber Developer’s Handbook [Sams 2004]

397Summary: Jabber and JXTA as Complimentary Technologies

Summary: Jabber and JXTA as ComplimentaryTechnologiesWe hope that you agree that JXTA and Jabber are technologies that approach the issuesof building P2P systems in different and complimentary ways.

The configuration of a Jabber network of servers is explicit and expressed in terms ofthe common Internet infrastructure of DNS and email-style (user@host) names.Thistype of configuration leverages the work done by countless others in building up theInternet naming structure and TCP/IP into a robust distributed environment. For long-lived services and other required infrastructure, this type of explicit configuration makessense.

JXTA’s discovery mechanism adds to the flexibility of a distributed system at theexpense of complexity. Discovering a service requires more complicated software thansimply referencing a JID from a configuration file, but for services that move or areintermittently available, this complexity is warranted. JXTA’s peer group concept enablesthe scoping of interactions without regard to the physical topology of the network.

Both these approaches are required to build a large-scale but flexible and dynamicpeer-to-peer application. Jabber’s simple and scalable infrastructure combined withJXTA’s dynamic configuration is a recipe for powerful P2P applications.

13 0672325365 CH10 1/21/05 2:26 PM Page 397

Page 415: Jabber Developer’s Handbook [Sams 2004]

13 0672325365 CH10 1/21/05 2:26 PM Page 398

Page 416: Jabber Developer’s Handbook [Sams 2004]

11Jabber Libraries for Popular

Languages

Most of life is choices, and the rest is pure dumb luck.

Marian Erickson

BECAUSE JABBER RELIES ON THE OPEN XML standard for its messaging formats, youhave lots of choices with regard to programming languages and Jabber libraries. In fact,this book has shown you examples in several different languages.This chapter looks atlibraries for creating Jabber-aware applications, shows some examples, and talks aboutwhere they might be appropriate.All these libraries are open-source and available as ofthis writing.

Jabber-Net—Jabber for the .NET EnvironmentMicrosoft’s .NET environment includes such popular languages as Visual Basic and C#(pronounced C-sharp).The Jabber-Net library (or assembly in .NET parlance) workswith any of the .NET languages to allow easy access to Jabber facilities. It is available athttp://www.jabberstudio.org/projects/jabber-net.

Jabber-Net provides a high-level API that isolates the programmer from the details of the Jabber XML protocol, making it well suited for quick development or drag-and-drop style programming with Visual Studio.To build a Jabber client, the class jab-ber.client.JabberClient is the main class you need to use. It has lots of properties,but the most important are listed in Table 11.1.

14 0672325365 CH11 1/21/05 2:26 PM Page 399

Page 417: Jabber Developer’s Handbook [Sams 2004]

400 Jabber Libraries for Popular Languages

Table 11.1 JabberClient Properties

Property Description

Agents The list of agents available on the server.

AutoAgents If true, automatically requests the server's list of agents when connecting.

AutoLogin If true, automatically logs in when connecting.

AutoPresence If true, automatically sends presence information when connecting.

AutoRoster If true, automatically retrieves roster information from the server when connecting.

Password The password to use to log in to the server.

Port The TCP/IP port on which to connect to the server.

Resource The resource to use when connecting to the server.

Server The name of the Jabber server to use when connecting.User The username to use when connecting to the server.

The other properties are covered in Jabber-Net’s online documentation.After you set the appropriate properties, calling the Connect() method causes the

JabberClient to initiate a connection with the server and exchange stream headers. Ifany of the Auth* properties are true, other packets may be exchanged also. Here’s a sim-ple example of connecting in C#:

JabberClient c = new JabberClient();

c.Server = “my-jabber”;

c.User = “user1”;

c.Password = “user1”;

c.Resource = “Work”;

c.AutoAgents = true;

c.AutoLogin = true;

c.AutoPresence = true;

c.AutoRoster = true;

c.Connect();

It’s important to note that the Connect() method doesn’t block until the client isconnected and the packets implied by the Auto* properties are exchanged.You need tocheck the IsAuthenticated property before assuming that sending a message will suc-ceed.

This library is especially useful for integrating other .NET or COM components intoJabber services. Listing 11.1 gives you an example of using Jabber .NET to drive anExcel spreadsheet from Jabber clients.

Listing 11.1 Jabber-Net in C#

1:using System;

2:using System.Threading;

3:using System.Reflection;

4:using jabber.client;

5:using jabber.protocol.client;

14 0672325365 CH11 1/21/05 2:26 PM Page 400

Page 418: Jabber Developer’s Handbook [Sams 2004]

401Jabber-Net—Jabber for the .NET Environment

6:using Excel;

7:

8:class MainClass {

9: JabberClient jabber;

10: Application excel;

11: _Worksheet sheet;

12: Range sumCell;

13: private int currentRow = 1;

14: private ManualResetEvent die = new ManualResetEvent(false);

15:

16: private void newMessage(object sender, Message msg) {

17: if (msg.Body == “Quit”) {

18: die.Set();

19: return;

20: }

21: double dValue = 0.0;

22: try {

23: dValue = Double.Parse(msg.Body);

24: } catch (Exception ex) {

25: Console.WriteLine(ex.StackTrace);

26: return;

27: }

28: double value = UpdateExcel(msg.From, dValue);

29: UpdatePresence(value);

30: }

31: bool connected = false;

32: private void newIQ(object sender, IQ iq) {

33: if ((!connected) && jabber.IsAuthenticated) {

34: connected = true;

35: UpdatePresence(0.0);

36: }

37: }

38: private void newPresence(object sender, Presence pres) {

39: if (pres.Type == PresenceType.subscribe) {

40: Presence response = new Presence(jabber.Document);

41: response.Type = PresenceType.subscribed;

42: response.To = pres.From;

43: jabber.Write(response);

44: }

45: }

46: public MainClass() {

47: excel = InitializeExcel();

48: jabber = InitializeJabber();

49: die.WaitOne();

50: jabber.Close();

51: excel.Quit();

Listing 11.1 Continued

14 0672325365 CH11 1/21/05 2:26 PM Page 401

Page 419: Jabber Developer’s Handbook [Sams 2004]

402 Jabber Libraries for Popular Languages

52: }

53:

54: private Application InitializeExcel() {

55: Application app = new Application();

56: app.Visible = true;

57: app.DisplayAlerts = false;

58: Workbooks workbooks = app.Workbooks;

59: _Workbook workbook = workbooks.Add(

60: XlWBATemplate.xlWBATWorksheet);

61: Sheets sheets = workbook.Worksheets;

62: sheet = (_Worksheet)sheets[1];

63: sumCell = sheet.get_Range(“C1”, Missing.Value);

64: sumCell.Formula = “=SUM(B:B)”;

65: return app;

66: }

67:

68: private double UpdateExcel(String who, double value) {

69: Range range = sheet.get_Range(

70: “A”+currentRow, “B”+currentRow);

71: object [] aRow = new object[2];

72: aRow[0] = who;

73: aRow[1] = value;

74: range.Value2 = aRow;

75: currentRow++;

76: return (double)sumCell.Value2;

77: }

78: private JabberClient InitializeJabber() {

79: JabberClient c = new JabberClient();

80: c.Server = “my-jabber”;

81: c.User = “excel”;

82: c.Password = “excel”;

83: c.Resource = “Work”;

84: c.AutoLogin = true;

85: c.Connect();

86: c.OnMessage += new MessageHandler(newMessage);

87: c.OnIQ += new IQHandler(newIQ);

88: c.OnPresence += new PresenceHandler(newPresence);

89: return c;

90: }

91: private void UpdatePresence(double val) {

92: jabber.Presence(PresenceType.available, “Sum=”+val,

93: “Available”, 0);

94: }

95: public static void Main(string[] args) {

96: new MainClass();

97: }

98:}

Listing 11.1 Continued

14 0672325365 CH11 1/21/05 2:26 PM Page 402

Page 420: Jabber Developer’s Handbook [Sams 2004]

403Jabber-Net—Jabber for the .NET Environment

This program functions as a Jabber client.When it starts, it opens a new Excel spread-sheet and initializes it to calculate the sum of one column. It accepts messages fromother clients and if the body of the message contains just a single number, it adds thatnumber and a record of the sender to the spreadsheet. Excel updates the sum, which theprogram reflects to clients by way of its presence information. So, the running total isalways reflected to everyone who is subscribed to the program’s presence. Figure 11.1lets you see what it looks like in WinJab.

Figure 11.1 Using presence information for status.

Notice that the mouseover data shows the Sum=702.The spreadsheet as managed by theclient is shown in Figure 11.2.

Figure 11.2 Maintaining a spreadsheet from Jabber.

Column A is the JID of the sender of each message and Column B is the value that wasin the message.This spreadsheet shows three messages, all from user1@my-jabber/Work,with values 123, 234, and 345.The total of Column B is in cell C1.This is the valuereflected in the presence.

14 0672325365 CH11 1/21/05 2:26 PM Page 403

Page 421: Jabber Developer’s Handbook [Sams 2004]

404 Jabber Libraries for Popular Languages

Let’s walk through some of the C# code. If you’re more comfortable with VisualBasic, the equivalent example follows (the line numbers are different, though). Start withthe constructor (starting at line 46). It just initializes Excel, initializes Jabber, waits for thequit signal, and closes each one.The InitializeExcel()(lines 54–66) method just setsup the spreadsheet as we described. Looking closer at InitializeJabber(), you can seethe important properties of the JabberClient object initialized at lines 80–84.We setthe AutoLogin property, so when we connect to the server (line 85) the library logs usin automatically.We don’t set the AutoPresence property because we want to handlepresence manually.

At lines 86–88, we install three event handlers to handle message packets, IQ packets,and presence packets, respectively. First, look at the newMessage() method at lines16–30. First it looks to see whether the entire message body is “Quit” and, if it is, wesignal the constructor to exit. Otherwise, we try to parse the message body into a double and use it to update the spreadsheet with the call to UpdateExcel() at line 28.UpdateExcel() returns the value in the summation column (C1), which we use toupdate the presence information in UpdatePresence(). UpdatePresence() itself is pret-ty simple (lines 91–94). It uses the JabberClient method Presence() to update thepresence information.This sends a presence packet to the server, which forwards it to allsubscribers.

The newIQ() method (lines 32–36) is called whenever the client receives an IQ pack-et. Because we know that we’ll receive an IQ packet when we successfully log in, we usethat occasion to initialize the presence information.

Before other clients can see the presence information, we have to allow them to sub-scribe to it.This is handled by the newPresence() method (lines 38–45), which is calledwhenever a presence packet is received.We accept all presence subscription requests, so ifthe packet received is a subscription request, we create a new presence packet of type“subscribed” and send it to the subscriber.

As promised, Listing 11.2 shows the equivalent example in Visual Basic.

Listing 11.2 Jabber-Net in Visual Basic

1:option strict off

2:imports System

3:imports System.Threading

4:imports System.Reflection

5:imports jabber.client

6:imports jabber.protocol.client

7:imports Excel

8:

9:class MainClass

10: public shared sub Main()

11: dim app as MainClass

12: app = new MainClass()

13: end sub

14:

14 0672325365 CH11 1/21/05 2:26 PM Page 404

Page 422: Jabber Developer’s Handbook [Sams 2004]

405Jabber-Net—Jabber for the .NET Environment

15: private jabber as JabberClient

16: private excel as Application

17: private sheet as _Worksheet

18: private sumCell as Range

19: private currentRow = 1

20: private die as new ManualResetEvent(false)

21:

22: private sub UpdatePresence(val as double)

23: jabber.Presence(PresenceType.available, _

24: String.Format(“Sum={0}”,val), “Available”, 0)

25: end sub

26: private sub newMessage(sender as object, msg as Message)

27: if (msg.Body.Equals(“Quit”)) then

28: die.Set()

29: return

30: end if

31: dim dValue as double

32: dvalue = 0.0

33: try

34: dValue = Double.Parse(msg.Body)

35: catch ex as Exception

36: Console.WriteLine(ex.StackTrace)

37: return

38: end try

39: dim value as double

40: value = UpdateExcel(msg.From.ToString(), dValue)

41: UpdatePresence(value)

42: end sub

43: private connected = false

44: private sub newIQ(sender as object, iq as IQ)

45: if ((not connected) and jabber.IsAuthenticated) then

46: connected = true

47: UpdatePresence(0.0)

48: end if

49: end sub

50: private sub newPresence(sender as object, pres as Presence)

51: if (pres.Type.Equals(PresenceType.subscribe)) then

52: dim response as Presence

53: response = new Presence(jabber.Document)

54: response.Type = PresenceType.subscribed

55: response.To = pres.From

56: jabber.Write(response)

57: end if

58: end sub

59: private function InitializeExcel() as Application

60: dim app as new Application()

Listing 11.2 Continued

14 0672325365 CH11 1/21/05 2:26 PM Page 405

Page 423: Jabber Developer’s Handbook [Sams 2004]

406 Jabber Libraries for Popular Languages

61: app.Visible = true

62: app.DisplayAlerts = false

63: dim workbooks as Workbooks

64: workbooks = app.Workbooks

65: dim workbook as _Workbook

66: workbook = workbooks.Add( XlWBATemplate.xlWBATWorksheet)

67: dim sheets as Sheets

68: sheets = workbook.Worksheets

69: sheet = sheets.Item(1)

70: sumCell = sheet.Range(“C1”, Missing.Value)

71: sumCell.Formula = “=SUM(B:B)”

72: return app

73: end function

74:

75: private function UpdateExcel(who as String, value as double) as double

76: dim range as Range

77: range = sheet.Range(String.Format(“A{0}”,currentRow), _

78: String.Format(“B{0}”,currentRow))

79: dim aRow(2) as object

80: aRow(0) = who

81: aRow(1) = value

82: range.Value2 = aRow

83: currentRow = currentRow + 1

84: return sumCell.Value2

85: end function

86: private function InitializeJabber() as JabberClient

87: dim c as new JabberClient()

88: c.Server = “my-jabber”

89: c.User = “excel”

90: c.Password = “excel”

91: c.Resource = “Work”

92: c.AutoLogin = true

93: c.Connect()

94: AddHandler c.OnMessage, addressOf newMessage

95: AddHandler c.OnIQ, addressof newIQ

96: AddHandler c.OnPresence, addressof newPresence

97: return c

98: end function

99: public sub New()

100: excel = InitializeExcel()

101: jabber = InitializeJabber()

102: die.WaitOne()

103: jabber.Close()

104: excel.Quit()

105: end sub

106:end class

Listing 11.2 Continued

14 0672325365 CH11 1/21/05 2:26 PM Page 406

Page 424: Jabber Developer’s Handbook [Sams 2004]

407Jabber-Net—Jabber for the .NET Environment

Building the ExamplesBuilding the C# and Visual Basic examples is a little complicated because you have toextract the type library information for the Excel COM component into a form that the.NET Framework can use.You need to know the path to your excel.exe orExcel9.olb, depending on your version of Microsoft Office, and run these commands:

NOTEIt’s probably not necessary to understand exactly what these commands do, so don’t sweat the details here.

If you’re using Visual Studio .NET, this is done automatically.

First create a key to use to register the Excel component:

c:> sn -k Excel.key

Microsoft (R) .NET Framework Strong Name Utility Version 1.0.3705.0

Copyright (C) Microsoft Corporation 1998-2001. All rights reserved.

Key pair written to Excel.key

Then extract the type library information and register it in the global assembly cache:

C:> tlbimp /silent “c:\Program Files\Microsoft Office\Office\Excel9.olb”

➥ /keyfile:Excel.key /out:Excel.DLL

C:> gacutil -i Excel.DLL

Microsoft (R) .NET Global Assembly Cache Utility. Version 1.0.3705.0

Copyright (C) Microsoft Corporation 1998-2001. All rights reserved.

Assembly successfully added to the cache

Then we can compile the C# code:

C:> csc /debug+ /r:Excel.DLL /r:jabber-net.dll /out:jabber-net-cs.exe Main.cs

Microsoft (R) Visual C# .NET Compiler version 7.00.9466

for Microsoft (R) .NET Framework version 1.0.3705

Copyright (C) Microsoft Corporation 2001. All rights reserved.

And the Visual Basic code:

C:> vbc /optionstrict- /debug+ /r:Excel.DLL /r:jabber-net.dll /r:System.dll

➥ /r:System.Xml.dll /out:jabber-net-vb.exe Main.vb

Microsoft (R) Visual Basic .NET Compiler version 7.00.9466

for Microsoft (R) .NET Framework version 1.00.3705

Copyright (C) Microsoft Corporation 1987-2001. All rights reserved.

C: >

Now there are two executables: jabber-net-cs and jabber-net-vb.They shouldbehave exactly the same. Run either one and try it out.

14 0672325365 CH11 1/21/05 2:26 PM Page 407

Page 425: Jabber Developer’s Handbook [Sams 2004]

408 Jabber Libraries for Popular Languages

iksemel—Jabber for C/C++A quick pass through www.jabberstudio.org or SourceForge confirms that there are afew options for writing Jabber clients and servers in C and C++. For the MicrosoftWindows environment, the JabberCOM library is used for some of the more popularinstant messaging clients. Jabber.com makes a package called JECL (Jabber ExternalComponent Libraries) for building server components under Unix. JECL is available onthe Jabber.com Web site at http://www.jabber.com. But we figure that the best reasonto write in C these days is to code close to the metal in a reasonably cross-platform way.For this reason, we’ll use this section to examine a very lightweight Jabber package callediksemel.

iksemel is available on the www.jabberstudio.orgWeb site. Download it and followthe simple instructions to build and install it. If you’re using Microsoft Windows, youmay want to get the free Cygwin (http://www.cygwin.com/) package to make thecompilation easier.

On Linux, the iksemel installation adds a new shared library to the system calledlibiksemel. So to compile a program that uses the library, you use a command like this:

gcc –g –liksemel –o ikdemo ikdemo.c

iksemel has the features that you’d expect from a Jabber library, including support forbuilding clients or services, SSL, and generating and handling the standard Jabber packettypes. Because this is C, we have to be aware of memory management, but fortunately,iksemel provides a memory pool that makes this a little easier.The great thing aboutthis package is that it’s tiny.The compiled library is only about 30KB—certainly smallenough to fit easily on a PDA, for example.

To try out iksemel, we’ll implement a very simple little program that just sends amessage to a user and exits.You might want to use something like this as a quick way tosend a message from a shell script.This command-line program takes several argumentsthat control what it does.When run without any arguments, it prints its usage:

$ ikdemo

USAGE: ikdemo <server> <username> <password> <resource> <destjid> <message>

Its six arguments aren server—The host name of the Jabber server.n username—The username to use when sending the message.n password—That user’s password.n resource—The resource to use when sending the message.n destjid—The JID of the user to receive the message.n message—The body of the message to be sent. If this is more than one word, it

needs to be enclosed in quotes.

14 0672325365 CH11 1/21/05 2:26 PM Page 408

Page 426: Jabber Developer’s Handbook [Sams 2004]

409iksemel—Jabber for C/C++

When the program runs, it logs in to the Jabber server, sends its message, and immediate-ly logs out. It ignores any messages it might receive.This is an example of the Jabberprinciple that a client needs to implement only that part of the protocol that it needs todo its job. Here’s an example run:

$ ikdemo my-jabber user1 user1 Work user2@my-jabber “Here’s a message from C”

user1@my-jabber/Work Sending Message to: user2@my-jabber

$

That’s all the client does.The recipient (user2@my-jabber, in this case) receives aheadline message, which WinJab displays in a separate tabbed frame as shown in Figure 11.3.

Figure 11.3 Receiving a headline message.

Simple enough; now let’s look at the code. Listing 11.3 shows the entire source.We’ll gothrough it step by step afterward.

Listing 11.3 Sending a Headline Message from C

1:#include <stdio.h>

2:#include “iksemel.h”

3:

4:void packetRecv(void *, ikspak *);

5:void usage(char *prog);

6:

7:typedef struct my_data {

8: char * server;

9: char * username;

10: char * password;

11: char * resource;

12: char * dest;

13: char *message;

14: iksparser *parser;

14 0672325365 CH11 1/21/05 2:26 PM Page 409

Page 427: Jabber Developer’s Handbook [Sams 2004]

410 Jabber Libraries for Popular Languages

15:} my_data;

16:

17:int main(int argc, char *argv[])

18:{

19: if (argc != 7) {

20: usage(argv[0]);

21: }

22: my_data *data = (my_data *)malloc(sizeof(my_data));

23: data->server = argv[1];

24: data->username = argv[2];

25: data->password = argv[3];

26: data->resource = argv[4];

27: data->dest = argv[5];

28: data->message = argv[6];

29: printf(“%s@%s/%s Sending Message to: %s\n”,

30: data->username, data->server, data->resource, data->dest);

31:

32: data->parser = iks_jabber_new(data, packetRecv);

33:

34: if (!iks_connect_tcp(data->parser, data->server, 0)) {

35: printf(“Error connecting to %s\n”,data->server);

36: exit(1);

37: }

38:

39: while(iks_recv(data->parser, 3000));

40: exit(0);

41:}

42:

43:

44:void usage(char *prog)

45:{

46: printf(“USAGE: %s <server> <username> “, prog);

47: printf(“<password> <resource> <destjid> <message>\n”);

48: exit(1);

49:}

50:

51:void packetRecv(void *d, ikspak *pkt)

52:{

53: my_data *data = (my_data *)d;

54: if (pkt->type == IKS_PAK_STREAM) {

55: iks* x;

56: char *jid = (char *)malloc(1024);

57: sprintf(jid, “%s@%s/%s”, data->username, data->server, data->resource);

58: iksid *id = iks_id_new(NULL, jid);

59:

60: x = iks_make_auth(id, data->password, iks_find_attrib(pkt->x, “id”));

61: iks_insert_attrib(x, “id”, “auth”);

Listing 11.3 Continued

14 0672325365 CH11 1/21/05 2:26 PM Page 410

Page 428: Jabber Developer’s Handbook [Sams 2004]

411iksemel—Jabber for C/C++

62: iks_send(data->parser, x);

63: iks_delete(x);

64: free(jid);

65: }

66: if (pkt->type == IKS_PAK_IQ) {

67: if (pkt->subtype == IKS_TYPE_ERROR) {

68: printf(“ERROR Authenticating\n”);

69: exit(1);

70: }

71: iks *x = iks_make_msg(IKS_TYPE_HEADLINE, data->dest,

72: data->message, “thread-id”);

73: iks_insert_cdata(iks_insert(x, “subject”),

74: “A Message From Me”, -1); // -1 for len means use strlen()

75: iks_send(data->parser, x);

76: iks_delete(x);

77:

78: iks_disconnect(data->parser);

79: }

80:

81:}

82:

The first two lines of the file include some definitions that we’ll need, including thestandard I/O functions and the iksemel library (iksemel.h). Following two forwarddeclarations of functions (lines 4 and 5), we define a structure that we’ll use to hold thecommand line data.

7:typedef struct my_data {

8: char * server;

9: char * username;

10: char * password;

11: char * resource;

12: char * dest;

13: char *message;

14: iksparser *parser;

15:} my_data;

The my_data structure also holds a reference to the XML parser that we’ll need tosend and receive packets.When we create the parser, we give it this structure and thefunction to call when a packet is received (named packedRecv):

32: data->parser = iks_jabber_new(data, packetRecv);

All that’s left to do in main() is connect to the server:

34: if (!iks_connect_tcp(data->parser, data->server, 0)) {

and process packets as they are received:

39: while(iks_recv(data->parser, 3000));

Listing 11.3 Continued

14 0672325365 CH11 1/21/05 2:26 PM Page 411

Page 429: Jabber Developer’s Handbook [Sams 2004]

412 Jabber Libraries for Popular Languages

The other action takes place when packets are received and the parser calls ourpacketRecv() function.The first thing we need to do after connecting is authenticate.Lines 54 through 65 in the packetRecv() function handle this.

54: if (pkt->type == IKS_PAK_STREAM) {

55: iks* x;

56: char *jid = (char *)malloc(1024);

57: sprintf(jid, “%s@%s/%s”, data->username, data->server, data->resource);

58: iksid *id = iks_id_new(NULL, jid);

59:

60: x = iks_make_auth(id, data->password, iks_find_attrib(pkt->x, “id”));

61: iks_insert_attrib(x, “id”, “auth”);

62: iks_send(data->parser, x);

63: iks_delete(x);

64: free(jid);

65: }

First, we check to make sure the incoming packet is the stream header (line 54). If itis, then we know that the next thing to do is authenticate, so we assemble our JID (lines55–58) and make an authentication packet (lines 60–61), using the password that westored in the data structure.Then we send the packet to the server (line 62) and de-allocate the memory that we used (lines 63–64).

NOTEYou may have noticed that we didn’t have to send a stream header. The iksemel library does that for us

when it connects.

After we send our authentication IQ packet, the server responds with an IQ packet thattells us whether or not the login was successful. If it was successful, we’ll go ahead andsend the message; otherwise we’ll exit with an error status.

66: if (pkt->type == IKS_PAK_IQ) {

67: if (pkt->subtype == IKS_TYPE_ERROR) {

68: printf(“ERROR Authenticating\n”);

69: exit(1);

70: }

71: iks *x = iks_make_msg(IKS_TYPE_HEADLINE, data->dest,

72: data->message, “thread-id”);

73: iks_insert_cdata(iks_insert(x, “subject”),

74: “A Message From Me”, -1); // -1 for len means use strlen()

75: iks_send(data->parser, x);

76: iks_delete(x);

Line 66 checks to make sure this is an IQ packet. If it is, we assume that it’s theresponse to our authentication packet and check to see whether it is an error response(line 67). Starting at line 71, we construct the message. Line 73 sets the subject (it’s oddthat the iks_make_msg function takes the thread-id as an argument, but you have to

14 0672325365 CH11 1/21/05 2:26 PM Page 412

Page 430: Jabber Developer’s Handbook [Sams 2004]

413JabberBeans—Jabber for Java

set the subject separately) and line 75 sends it on its way. Line 76 cleans up the allocatedpacket memory.

Finally, after sending the message, we disconnect from the server:

78: iks_disconnect(data->parser);

This causes the iks_recv() function to return 0 and the main() function exits.

39: while(iks_recv(data->parser, 3000));

40: exit(0);

The iksemel library is not as fully-featured as those for some of the other languages,but for simple applications where those features are not needed, or those in which multi-megabyte libraries are too big, iksemel is a nice alternative.

JabberBeans—Jabber for JavaThe JabberBeans library is a very complete library for building Jabber clients and servercomponents in Java.We’ve seen many examples in this book that use the JabberBeanslibrary, so we won’t do another one here.We’ve found it to be a very robust and power-ful library, with support for everything we’ve needed.

Like most of the libraries in this chapter, JabberBeans is available on thehttp://www.jabberstudio.orgWeb site. It comes packaged as a single Java archive(JAR) file, which just needs to be added to the Java CLASSPATH of your Jabber program.

JabberBeans contains over 200 classes and interfaces for building all types of Jabberpackets and interactions. It even includes support for the <xdb> and <route> packets,which are rarely used by server component developers (see Chapter 5,“Extending theJabber Server,” for examples).

Because it is so comprehensive and flexible, however, JabberBeans can be more diffi-cult to use than other Jabber libraries. For example, simply sending a message may takeseveral lines of code in JabberBeans:

MessageBuilder mb = new MessageBuilder();

mb.setToAddress(fromAddress);

mb.setFromAddress(toAddress);

mb.setSubject(subject);

mb.setThread(thread);

mb.setType(type);

mb.setBody(“Greetings”);

try {

cb.send(mb.build());

} catch (InstantiationException e) {

}

Contrast this with a similar example that uses the jabberlib TCL library:

jlib::send_msg $toAddress -type $type -thread $thread \

-subject $subject -body “Greetings”

14 0672325365 CH11 1/21/05 2:26 PM Page 413

Page 431: Jabber Developer’s Handbook [Sams 2004]

414 Jabber Libraries for Popular Languages

So although there may be nothing you can’t do with JabberBeans, it might take quitea few lines of code to do it. Of course, it’s likely that a project that used JabberBeanswould define helper methods to encapsulate frequently-used functionality like this.

Still, there’s a reason we chose JabberBeans for many of the examples in this book—it’s a very robust and mature library.

JabberPy—Jabber for PythonThe other language that we use for examples in this book is Python.The number ofextension modules available for Python is staggering and jabber.py is an excellentexample. It can be used to build both clients and server components.The library is avail-able from http://jabberpy.sourceforge.net, as are some good examples.To install itinto your Python distribution, just unpack the archive file and run

# python setup.py install

If you don’t want to install it with your Python site libraries, you can just include thetwo files xmlstream.py and jabber.py in your $PYTHONPATH.

jabber.py can do most anything you’re likely to need in a lightweight, easy-to-uselanguage.

A Cross-Language ExampleThe next three sections cover some Jabber libraries for some other scripting languages:TCL, Perl, and Ruby.We’ll present the same example in all three languages so you cansee how they compare.This example of a simple Jabber chat client shows the basics ofconnecting to the server, sending and receiving messages, and subscribing and exchang-ing presence information.

Each of these three languages support the TK user interface widget library, so theexamples are structured pretty much the same and we won’t belabor the GUI setupcode.

Before diving into the code, let’s look at how the example works. It brings up a smallwindow with a familiar chat window layout.The row of text entry boxes along the bot-tom enable the user to enter his Jabber ID, password, and the Jabber ID of the personwith which to chat. Figure 11.4 shows the initial window.

The Connect button on the bottom right causes the client to connect to the serverwhen clicked.While connected, the client can send a message to the other Jabber clientby typing it in the box just above the connection information.These messages, as well asmessages and presence information from the other client, are displayed in the top, scrolla-ble window. Figure 11.5 shows a chat session in progress.

14 0672325365 CH11 1/21/05 2:26 PM Page 414

Page 432: Jabber Developer’s Handbook [Sams 2004]

415A Cross-Language Example

Figure 11.4 The initial Chat window.

Figure 11.5 A simple chat client.

This dialog shows that the client (user1@my-jabber) connected and received a rosterupdate that said the other client (user2@my-jabber) was online. Notice that the sourceof the message is the first token on each line (enclosed in <>). User1 sent the message,“Hello, user” and user2 replied “Hello to you, user1,” followed by “I’m going to go tolunch now.” Finally, user2 changed his presence show to “away” and his status “Lunch.”This is reflected in the last line in the scrolled window.

After the client is connected, the label on the button changes to Send Presence.Whenthe button is clicked, it updates the client’s presence status from the default “Online” to“Let’s Roll.” User2 can see this in his IM client, as shown in Figure 11.6.

Now look at the code that sets up the GUI. Because all three languages use TK, thiscode is much the same for all three examples.We’ll go through the TCL GUI setup indetail and the others are available with the example code.

14 0672325365 CH11 1/21/05 2:26 PM Page 415

Page 433: Jabber Developer’s Handbook [Sams 2004]

416 Jabber Libraries for Popular Languages

Figure 11.6 Changed presence.

Listing 11.4 Creating the User Interface

1:proc make_gui {} {

2: global b

3: global jid

4: global pass

5: global otherJid

6: global chatEntry

7: global output

8:

9: set jid “”

10: set pass “”

11: set otherJid “”

12: set chatEntry “”

13:

14: # Create a root window with three partitions in it

15: wm title . “Jabber Chat”

16:

17: frame .button_frame

18: pack .button_frame -side bottom

19: frame .bframe

20: pack .bframe -side bottom

21: frame .tframe

22: pack .tframe -side top

23:

24: # The bottom frame gets a button and three text boxes

25: frame .button_frame.lframe

26: pack .button_frame.lframe -side left

27: label .button_frame.lframe.lab -text “Your JID”

28: pack .button_frame.lframe.lab -side left

29: entry .button_frame.lframe.jid_entry -textvariable jid

30: pack .button_frame.lframe.jid_entry -side right

31:

14 0672325365 CH11 1/21/05 2:26 PM Page 416

Page 434: Jabber Developer’s Handbook [Sams 2004]

417A Cross-Language Example

32: frame .button_frame.lframe2

33: pack .button_frame.lframe2 -side left

34: label .button_frame.lframe2.lab -text “Password “

35: pack .button_frame.lframe2.lab -side left

36: entry .button_frame.lframe2.pass_entry -textvariable pass

37: pack .button_frame.lframe2.pass_entry -side right

38:

39: frame .button_frame.lframe3

40: pack .button_frame.lframe3 -side left

41: label .button_frame.lframe3.lab -text “Other JID”

42: pack .button_frame.lframe3.lab -side left

43: entry .button_frame.lframe3.pass_entry -textvariable otherJid

44: pack .button_frame.lframe3.pass_entry -side right

45:

46: button .button_frame.b -text “Connect” -command buttonPress

47: set b .button_frame.b

48: pack .button_frame.b -side right

49:

50: # The top frame gets a multi-line scrolled text window to show messages

51: text .tframe.text -width 80 -height 20 -yscrollcommand “.tframe.scroll set”

52: scrollbar .tframe.scroll -command “.tframe.text yview”

53: pack .tframe.scroll -fill y -side left

54: pack .tframe.text -side top -fill both -anchor n -expand true

55: set output .tframe.text

56:

57: .tframe.text tag configure mine -foreground red

58: .tframe.text tag configure his -foreground blue

59:

60: # The bottom frame gets a single-line text window for message entry

61: entry .bframe.text -width 80 -textvariable chatEntry

62: pack .bframe.text -side bottom -fill both -anchor s

63:

64: bind .bframe.text <Return> sendMsg

65:}

The first dozen lines of this example declare and initialize some global variables.The restof this function is mostly concerned with creating and positioning the various GUI ele-ments, but there are a couple interesting points.The TK library allows variables to beassociated with widgets so that when someone changes the widget (by entering text, forexample), the associated variable is automatically updated. Likewise, when the value ofthe variable changes, the widget is automatically updated.This line associates the variablenamed jid with the appropriate text entry widget:

29: entry .button_frame.lframe.jid_entry -textvariable jid

Listing 11.4 Continued

14 0672325365 CH11 1/21/05 2:26 PM Page 417

Page 435: Jabber Developer’s Handbook [Sams 2004]

418 Jabber Libraries for Popular Languages

So the jid variable will always reflect the contents of that text entry widget.The large, scrolled window will contain incoming and outgoing messages, as well as

presence status messages.To distinguish the messages, we define two TK tags that we canuse to make the text red (for outgoing messages) or blue (for incoming messages):

57: .tframe.text tag configure mine -foreground red

58: .tframe.text tag configure his -foreground blue

Presence information will use the default black text color.You can associate functions with TK buttons by using the –command option.This line

causes the function buttonPress to be called when the button is pressed:

46: button .button_frame.b -text “Connect” -command buttonPress

The buttonPress function does one of two things, depending on whether the clientis connected or not. Here’s the code for buttonPress:

proc buttonPress {} {

global b

global jid

global pass

global otherJid

set curLabel [$b cget -text]

# A little cheesy. The first time the button is pushed, it

# connects. All subsequent times, it sends the presence.

if {[string compare $curLabel “Connect”] == 0} {

connectToJabber $jid $pass $otherJid

$b configure -text “Send\nPresence”

} else {

sendPresence

}

}

It checks to see whether the button’s label is Connect and, if it is, calls theconnectToJabber function and changes the button label to Send Presence. Otherwise,it calls the sendPresence function.

You associate key presses with functions by using the TK bind command.This linecauses the sendMsg function to be called when the <Return> key is pressed in the mes-sage entry box:

64: bind .bframe.text <Return> sendMsg

That’s all there is to the GUI. Now the following section looks at the individualJabber client libraries.

Jabberlib—Jabber for TCLJabberlib has been around for a while and, although it is incorporated in several projects,it differs from the other libraries explored in this chapter in that it isn’t maintained as aseparate library.We’ve found that the Jabberlib included with the Tkabber client worksquite well.

14 0672325365 CH11 1/21/05 2:26 PM Page 418

Page 436: Jabber Developer’s Handbook [Sams 2004]

419A Cross-Language Example

Tkabber is available from http://www.jabberstudio.org. Download it and copy thejabberlib-tclxml to the TCL lib directory. It includes a pkgIndex.tcl file that tellsthe TCL interpreter about Jabberlib.After Jabberlib is installed, you only need to includelines like these in your script to use it:

package require jabberlib

package require Tclx

The connectToJabber function establishes the connection to the Jabber server.

1:proc connectToJabber {myjid passwd chatjid} {

2: global output

3: global auth_result

4: set start_idx [expr [string first “@” $myjid] + 1]

5: set end_idx [expr [string first “/” $myjid] - 1]

6: set server [string range $myjid $start_idx $end_idx]

7: set idx [expr [string first “@” $myjid] - 1]

8: set user [string range $myjid 0 $idx]

9: set sock [socket $server 5222]

10: jlib::connect $sock $server

11: jlib::send_auth $user $passwd aresource recv_auth_result

12: vwait auth_result

13: if {$auth_result == “OK”} {

14: $output insert end “Connected\n”

15: }

16: jlib::send_presence -stat Online

17: jlib::roster_get -command roster_cmd

18:}

Lines 4 through 8 parse the user’s JID into username, server name, and resource.These are used to connect and authenticate to the server. Line 9 creates the TCP socketthat we’ll use to exchange messages with the server, and line 10 makes the connection.

Line 11 uses the user JID and password to authenticate to the server. Notice the lastparameter to jlib::send_auth is a callback function that is called when the authentica-tion response packet is received.

proc recv_auth_result {res args} {

global auth_result

if {$res == “OK”} {

set auth_result OK

} else {

set auth_result ERR

}

}

14 0672325365 CH11 1/21/05 2:26 PM Page 419

Page 437: Jabber Developer’s Handbook [Sams 2004]

420 Jabber Libraries for Popular Languages

The auth_result method sets the global auth_result flag based on the server’sresponse. Line 12 in connectToJabber waits until the auth_result variable is set. Line16 sends our initial presence information, and line 17 requests our roster.Theroster_cmd function is called when the roster is delivered from the server.

proc roster_cmd {status} {

#status is BEGIN_ROSTER or END_ROSTER

# END_ROSTER means the roster is ready to use

echo “Roster CMD: $status\n”

}

This client doesn’t have any use for the roster, so it is ignored.While connected, the client can send messages to the other client.The sendMsg

function is called when the user presses <Return> in the message entry field.

1: proc sendMsg {} {

2: global chatEntry

3: global output

4: global jid

5: global otherJid

6: set text $chatEntry

7: set idx [expr [string first “@” $jid] - 1]

8: set uname [string range $jid 0 $idx]

9: $output insert end “<$uname> $text\n” mine

10: jlib::send_msg $otherJid -type chat -thread chat_demo_thread \

11: -subject chat_demo_subject -body $text

12: set chatEntry “”

13:}

sendMsg uses several of the global variables to fill out the message fields. First it insertsthe outgoing message into the log window by using the tag that represents outgoingmessages (line 9), then it sends the message by using the jlib::send_msg command(line 10), and resets the contents of the text entry box (line 12).

This version of Jabberlib defines that incoming messages are received at a functioncalled client:message.

proc client:message {from id type subject body err thread priority x} {

global output

set idx [expr [string first “@” $from] - 1]

set uname [string range $from 0 $idx]

$output insert end “<$uname> $body\n” his

}

This function accepts a lot of arguments, but we’re interested in only a couple.Themethod parses the “from” address to get the username to display at the beginning of theline. Lastly, it inserts the body of the message at the end of the output window by usingthe TK tag for incoming messages.

14 0672325365 CH11 1/21/05 2:26 PM Page 420

Page 438: Jabber Developer’s Handbook [Sams 2004]

421A Cross-Language Example

Updating our presence is simple.The sendPresence function does that:

1:proc sendPresence {} {

2: jlib::send_presence -show chat -stat {Let’s Roll}

3:}

Responding to presence subscriptions is a little more complicated.We need to replyto “subscribe” and “unsubscribe” presence packets and request our own subscriptions aswell.The version of Jabberlib that we’re using calls this method client:presence:

4:proc client:presence {from type x args} {

5: switch — $type {

6: subscribe {

7: jlib::send_presence -to $from -type subscribed

8: jlib::send_presence -to $from -type subscribe

9: }

10: unsubscribe {

11: jlib::send_presence -to $from -type unsubscribed

12: jlib::send_presence -to $from -type unsubscribe

13: }

14: default {

15: global output

16: set show “”

17: set status “”

18: foreach {attr val} $args {

19: if {[string match -show $attr]} {set show $val}

20: if {[string match -status $attr]} {set status $val}

21: }

22: if {[string length $show] == 0} {set show “normal”}

23: if {[string length $status] == 0} {set status “Online”}

24: $output insert end “<roster> $from is $show/$status\n”

25: }

26: }

27:}

Lines 6 through 13 handle subscribe and unsubscribe requests.All other presencepackets get displayed in the output window prefixed by <roster> (line 24). Notice thatthe arguments come in the form of a hash ($args) and that show and status may beunspecified. If either is not specified, we set their default values (lines 22–23).

That’s it.Although the TCL Jabberlib may not be as well packaged as some others, ithas lots of good functionality for building clients in TCL.

Net::Jabber—Jabber for PerlYou can build server components as well as clients with the Net::Jabber library forPerl. It is available from http://www.jabberstudio.org as well as the CPAN perl

14 0672325365 CH11 1/21/05 2:26 PM Page 421

Page 439: Jabber Developer’s Handbook [Sams 2004]

422 Jabber Libraries for Popular Languages

archive and depends on a couple other Perl packages: the XML::Stream package imple-ments Jabber’s XML stream protocol, and the Digest::SHA1 package implements thecryptographic hash function required for authentication. XML::Stream is also on theJabberStudio site, and Digest::SHA1 is included with most Perl distributions.

Unpack the downloaded archive and run these commands to install Net::Jabber:

% perl Makefile.PL

% make

% make install

You may have to be root to do the make install, depending on your Perl installa-tion.After the libraries are installed, you can build a client by including this line in yourPerl source code:

use Net::Jabber ‘Client’;

The Perl connectToJabber function is straightforward.

1:sub connectToJabber() {

2: my $uname;

3: my $server;

4: my $resource;

5: ($uname, $server, $resource) = ($jid =~/([^@]*)@([^\/]*)\/(.*)/);

6: $connection = new Net::Jabber::Client();

7: $connection->SetCallBacks(message=>\&messageCB,

8: presence=>\&presenceCB);

9: my $status = $connection->Connect(hostname=>$server);

10:

11: my @result = $connection->AuthSend(username=>$uname,

12: password=>$pass,

13: resource=>$resource);

14: if ($result[0] ne “ok”)

15: {

16: print “ERROR: Authorization failed: $result[0] - $result[1]\n”;

17: exit(0);

18: }

19: $connection->RosterGet();

20: $connection->PresenceSend();

21:

22: # Look for jabber messages every 1000 millisecs

23: my $id = Tk::After->new($text, 1000, ‘repeat’, \&pollJabber);

24:}

Line 5 uses Perl’s powerful regular expression capability to parse the user’s JID intousername, server name, and resource. Line 6 makes a new Jabber client connection

14 0672325365 CH11 1/21/05 2:26 PM Page 422

Page 440: Jabber Developer’s Handbook [Sams 2004]

423A Cross-Language Example

object, and line 7 tells it which methods should be called when message and presencepackets arrive: messageCB will be called for message packets and presenceCB will becalled for presence packets.

Line 9 makes the connection to the server, and line 11 authenticates. If the authenti-cation result is not “ok” (line 14), we simply give up and exit. Otherwise, we proceed toget our roster (line 19) and send our initial presence information (line 20).We need toservice both the Jabber connection and the GUI, so we can’t just wait for Jabber mes-sages. Line 23 sets up a timer that will look for Jabber messages once per second by calling the pollJabber function.This should be often enough that any delay is notnoticeable and still allow us time to service button clicks and the like.

sub pollJabber {

my $res = 1;

while ($res) {$res = $connection->Process(0);}

}

pollJabber processes all Jabber packets until no more are available (that is, until$connection->Process returns zero).

The sendMsg function is called when the user presses <Return> in the message entryfield.

sub sendMsg {

$connection->MessageSend(to=>$otherJid, subject=>”chat_demo_subject”,

thread=>”chat_demo_thread”,type=>”chat”,

body=>$chatEntry);

my $uname;

my $server;

my $resource;

($uname, $server, $resource) = ($jid =~/([^@]*)@([^\/]*)\/(.*)/);

$text->insert(“end”, “<$uname> $chatEntry\n”, ‘mine’);

$chatEntry = ‘’;

}

The hash arguments to $connection->MessageSend configure the destinationaddress, subject, thread, type, and body of the message.The next five lines parse the user-name from the JID and format it and the message text into a line in the output window,using the TK tag for outgoing messages.The last line resets the message entry field to beempty.

Incoming messages are delivered to the messageCB function.

sub messageCB {

my $sid = shift;

my $msg = shift;

my $src = $msg->GetFrom(“jid”)->GetUserID();

my $msgtxt = $msg->GetBody();

$text->insert(“end”, “<$src> $msgtxt\n”, ‘his’);

}

14 0672325365 CH11 1/21/05 2:26 PM Page 423

Page 441: Jabber Developer’s Handbook [Sams 2004]

424 Jabber Libraries for Popular Languages

This function uses the GetUserID() method of the JID object to extract the user-name of the sending client.The last line of the function inserts the body of the messageat the end of the output text box by using the TK tag for incoming messages.

The sendPresence function adds the source JID and passes its other arguments on tothe $connection->PresenceSend method.

1:sub sendPresence {

2: my %args;

3: while($#_ >= 0) { $args{ lc pop(@_) } = pop(@_); }

4: $args{from} = $jid;

5: $connection->PresenceSend(%args);

6:}

The presenceCB function is called when a presence packet is received.

7:sub presenceCB {

8: my $sid = shift;

9: my $presence = shift;

10: my $from = $presence->GetFrom();

11: my $type = $presence->GetType();

12: switch ($type) {

13: case “subscribe” {

14: sendPresence(to => $from, type => “subscribed”);

15: sendPresence(to => $from, type => “subscribe”);}

16: case “unsubscribe” {

17: sendPresence(to => $from, type => “unsubscribed”);

18: sendPresence(to => $from, type => “unsubscribe”);}

19: }

20: my $show = $presence->GetShow();

21: my $status = $presence->GetStatus();

22: if ($show or $status) {

23: $show = “normal” unless $show;

24: $text->insert(“end”, “<roster> $from is now $show/$status\n”);

25: }

26:}

If the presence packet is a subscription request, it is accepted and a reciprocal sub-scription is requested (lines 13–19). Otherwise, the show and status elements of thepresence packet are extracted and appended to the output window text (lines 20–24).

As you can see, the Net::Jabber package is very powerful and complete. Many Perlaficionados have put it to good use for building Jabber clients and server components.

Jabber4R—Jabber for RubyRuby is a relative newcomer to the list of popular scripting languages. It combines manyof the useful features of Python and Perl and can be used to build Jabber clients with theaddition of the jabber4r library.

14 0672325365 CH11 1/21/05 2:26 PM Page 424

Page 442: Jabber Developer’s Handbook [Sams 2004]

425A Cross-Language Example

jabber4r is available at http://www.jabberstudio.org and installs easily into yourRuby distribution. Unpack the jabber4r archive and run:

% ruby install.rb

Although jabber4r doesn’t claim to be as complete as other Jabber libraries, it still hasthe capabilities we need to implement our little chat client.To make the library availablein your Ruby application, just add this line to the top of your script:

require ‘jabber4r/jabber4r’

First, let’s look at the connectToJabber method, which makes the connection to theJabber server and authenticates our user.

1:def connectToJabber(myjid, passwd, chatjid)

2: begin

3: $session = Jabber::Session.bind_digest(myjid, passwd)

4: $session.announce_initial_presence

5: $dest = chatjid

6: $myjid = myjid

7: $session.add_roster_listener {|event, roster|

8: rosterCB(event, roster)

9: }

10: $session.add_message_listener {|msg|

11: messageCB(msg.body)

12: }

13: $session.set_subscription_handler {|subscription|

14: subscriptionCB(subscription)

15: }

16: rescue

17: print “Exception: “+$!.backtrace.join(“\n”)

18: end

19:end

jabber4r tries to hide some of the drudgery of initiating a Jabber session by encapsu-lating the connecting and authentication into the bind_digest method in line 3. Unlikein other scripting languages, it parses the user’s JID (myjid in this case) to find the Jabberserver’s hostname.Also the announce_initial_presence method (line 4) handles therouting initialization of the presence mechanism.

The next three statements add listeners for roster, message, and subscription events(lines 7, 10, and 13). jabber4r separates <presence> packets and sends subscribe,unsubscribe, subscribed, and unsubscribed packets to the subscription handler in theform of Subscription objects, and others to the roster listener in the form of Rosterobjects.

Because the TK –textvariable option is not supported by the Ruby/TK binding (asof this writing), the sendMsg method extracts the message body text directly from thetext entry widget (x.widget line 2) and appends it to the output text widget (line 4),using the TK tag for incoming messages.

1:def sendMsg(x)

2: text = x.widget.value().strip

14 0672325365 CH11 1/21/05 2:26 PM Page 425

Page 443: Jabber Developer’s Handbook [Sams 2004]

426 Jabber Libraries for Popular Languages

3: $input.delete(“0”, “end”)

4: $output.insert(“end”, “<#{$myjid.split(‘@’)[0]}> #{text}\n”, “mine”)

5: msg = $session.new_chat_message($dest)

6: msg.set_body(text)

7: msg.set_thread(“chat_demo_thread”)

8: msg.set_subject(“chat_demo_subject”)

9: msg.send(false)

10:end

Lines 5–8 construct a new chat message, and line 9 sends it on its way.The (false)parameter to msg.send tells jabber4r not to wait for a reply. If we passed the parametertrue, the send method would return the response message.

When messages are received, the messageCB method is called.

def messageCB(text)

$output.insert(“end”, “<#{$dest.split(‘@’)[0]}> #{text}\n”, “his”)

end

It simply parses the username from the JID of our correspondent and appends themessage body by using the TK tag for incoming messages.

When the Send Presence button is clicked, the sendPresence method sends theupdated presence information.

def sendPresence

presence = Jabber::Protocol::Presence.new(

Jabber.gen_random_id, “chat”, “Let’s Roll”)

$session.connection.send(presence)

end

It simply creates a new Presence object and sends it by using the global$session.connection.

As we mentioned, jabber4r divides presence handling into two parts: presence sub-scription handling and presence status updates.The subscriptionCB method handlesclients’ requests to subscribe to our presence.

1:def subscriptionCB(subscription)

2: case subscription.type

3: when :subscribe

4: subscription.accept

5: $session.subscribe(subscription.from)

6: when :unsubscribe

7: subscription.accept

8: p = Jabber::Protocol::Presence.new(Jabber.gen_random_id)

9: p.type = “unsubscribe”

10: p.to = subscription.from

11: $session.connection.send(p)

12: end

13:end

14 0672325365 CH11 1/21/05 2:26 PM Page 426

Page 444: Jabber Developer’s Handbook [Sams 2004]

427Summary

The first section of this case statement handles subscription requests (that is, the typeof the presence packet equals subscribe). Line 4 uses the jabber4r convenience methodSubscription.accept to respond with a packet that accepts the subscription. Line 5requests a subscription to the other’s presences by using the convenience method sub-scribe on the $session object.

The second section of subscriptionCB handles unsubscribe requests.Again we usethe Subscription.accept method to respond positively (line 7), but jabbber4r has no$session.unsubscribe convenience method, so we just build a presence packet manu-ally (lines 8–10) and send it ourselves (line 11).

The other aspect of handling presence packets is dealing with updates in others’ pres-ence status.This information is routed to the rosterCB method.

1:def rosterCB(event,roster)

2: action = case event

3: when Jabber::Roster::ITEM_ADDED

4: “item added”

5: when Jabber::Roster::ITEM_DELETED

6: “item deleted”

7: when Jabber::Roster::RESOURCE_ADDED

8: “resource added”

9: when Jabber::Roster::RESOURCE_UPDATED

10: “resource updated”

11: when Jabber::Roster::RESOURCE_DELETED

12: “resource deleted”

13: end

14: text = “<roster #{action}>”

15: text << “#{$dest}/#{roster.name} is “

16: text << “#{roster.show}:#{roster.status}\n”

17: $output.insert(“end”, text)

18:end

The case statement at the beginning of this method (lines 2–13) converts the jab-ber4r roster event type into its text equivalent.Then the relevant items from the Rosterobject (such as show and status) are appended to the output window text (lines 14–17).

Although the jabber4r library is not as complete as some of the other libraries, it doesa good job of hiding some of the complexity that can accompany speaking the Jabberprotocol. If Ruby is your language of choice, jabber4r is a fine way to build Jabber clientfunctionality.

SummaryIn this chapter, we’ve surveyed libraries for building Jabber clients and server compo-nents in languages appropriate for almost any environment.At the low end, C iksemellibrary is suitable for all but the tiniest networked devices and provides the capabilities

14 0672325365 CH11 1/21/05 2:26 PM Page 427

Page 445: Jabber Developer’s Handbook [Sams 2004]

428 Jabber Libraries for Popular Languages

needed to speak any subset of the protocol.At a higher level, the Java JabberBeans and.NET Jabber-Net libraries provide Jabber functionality for those popular enterprise-tierlanguages. Finally, we saw examples of libraries for scripting languages that support rapiddevelopment of Jabber applications.The jabber.py library can be used to implementclients and server components in the Python programming language. Jabberlib supportsbuilding Jabber clients in the venerable TCL scripting language. Perl is well supported bythe Net::Jabber library for building clients and server components.The newcomer,Ruby, gets its Jabber functionality from the jabber4r library.Whatever your require-ments, we hope that this chapter has shown you an option that fills the bill.

14 0672325365 CH11 1/21/05 2:26 PM Page 428

Page 446: Jabber Developer’s Handbook [Sams 2004]

III Appendixes

A Glossary

B XML Basics

C Resources

15 0672325365 Pt 3 1/21/05 2:26 PM Page 429

Page 447: Jabber Developer’s Handbook [Sams 2004]

15 0672325365 Pt 3 1/21/05 2:26 PM Page 430

Page 448: Jabber Developer’s Handbook [Sams 2004]

This appendix defines some useful con-cepts and terms that may be unfamiliarto some. Understanding these terms isimportant to understanding the topics ofthis book.Authentication The process of verify-ing the identity of a person or program.Authorization The process of deter-mining the privileges allowed to a personor program.Client In general, a program that usesthe services of network servers. In instantmessaging, an interactive program thatenables its user to send messages, partici-pate in chat sessions, and so on.Certificate, Public Key A piece ofdata containing information about theidentity of a person or program and oneor more cryptographic keys assigned tothat entity.The certificate is digitallysigned by a trusted authority to ensure itsintegrity.Confidentiality An information secu-rity term meaning that the contents of atransmission are hidden from all partiesexcept the sender and the receiver.Extensible Markup Language SeeXML.Extensible Messaging and PresenceProtocol See XMPP.DOM (Document Object Model)A method of parsing XML documents inwhich a hierarchical object is constructedthat represents the elements of an XMLdocument.The entire document is

processed and the DOM constructed inone operation.This method of processingXML is not appropriate for Jabberbecause an entire session consists of oneXML document and the elements of thatdocument are packets that must be inter-preted immediately.Gateway In Jabber, a service that pro-vides translation between the Jabber pro-tocols and the protocols of anotherinstant-messaging system such as AIM,IRC, and so on.Instant Messaging A network appli-cation that provides services to allowinteractive exchange of messages betweenpeople.The original application ofJabber.Integrity An information securityterm meaning that the contents of atransmission have not been altered intransmission in a way that is notdetectable to the recipient.IP Address (Internet ProtocolAddress) A unique numerical identifi-er assigned to each computer on theInternet.The most common type of IPaddress is the version 4 address (some-times called IPv4), which is a 32-bitnumber represented as four 8-bit quanti-ties separated by dots. For example:192.168.1.1 and 127.0.0.1.Loopback Address A special IPaddress that refers to the current host.That is, on any computer, an applicationconnecting to the loopback address

Glossary

16 0672325365 AppA/Glossary 1/21/05 2:26 PM Page 431

Page 449: Jabber Developer’s Handbook [Sams 2004]

432 Free Software

connects to an application running onthe same host.The loopback address is127.0.0.1.Free Software As distinguished fromopen source, a business model of softwaredevelopment in which the developergives all source code and distributionrights to all, with the exception of theright to redistribute the software underdifferent conditions (such as for a fee).Open Source Software A businessmodel of software development in whichthe developer distributes software insource code form under varying condi-tions of use. In its most common form,source code is distributed without feeunder the condition that it can be incor-porated into commercial products, butmodifications to that original code mustbe returned to the original developer(s)for distribution to others without fee.Peer-to-Peer (P2P) An architecturefor distributed applications in which anyentity is capable of acting as a client anda server.Port A 16-bit unsigned number thatidentifies an endpoint for network com-munications.When combined with an IPaddress, it uniquely defines a networklocation.Roster A list of contacts maintained byan instant messaging client. Jabber storesa user’s roster on the server so it is avail-able from any location.RSS (RDF, or Rich, Site Summary)An XML format for publishing newsinformation (or information in a news-like format) in an easily machine-readable format. Seehttp://web.resource.org/rss/.

SAX (Simple API for XML) Amethod for parsing XML documents inwhich callback methods are invoked asthe elements of the document arescanned. Because the Jabber protocol callsfor the immediate interpretation of XMLelements as Jabber packets, this is themethod used by Jabber XML parsers. Seealso DOM.Server A network application withcontinuous presence that offers servicesto clients.Typically, multiple clients canuse the same server simultaneously.Session The series of interactionsbetween a Jabber client and a serverbetween the time the client is authenticated (logged in) and the time itdisconnects.Socket A virtual stream between twonetwork endpoints defined by theirrespective IP addresses and ports.TCP/IP (Transmission ControlProtocol/Internet Protocol)TCP/IP is the most common networkprotocol on the Internet and defineswire-level binary formats for networkpackets that can be used to establish vir-tual streams (sockets) for reliablyexchanging information between net-work applications.These streams are usedto implement many other protocols,including SMTP (email), FTP (file trans-fer), HTTP (Web access), and the Jabberprotocols.Web Services A distributed application architecture in which loosely-coupled elements communicate usingstandard non-proprietary World WideWeb protocols such as XML and HTTPrather than proprietary or custom protocols.

16 0672325365 AppA/Glossary 1/21/05 2:26 PM Page 432

Page 450: Jabber Developer’s Handbook [Sams 2004]

433XMPP

XML Extensible Markup LanguageA subset of Simple Generalized MarkupLanguage (SGML), it is used in Jabber toformat messages and configuration files.It is a text format rather than a binaryformat, so it is readable and editable withcommon tools. See also Appendix C.XMPP The name of the XML proto-col used by Jabber clients and servers.

16 0672325365 AppA/Glossary 1/21/05 2:26 PM Page 433

Page 451: Jabber Developer’s Handbook [Sams 2004]

16 0672325365 AppA/Glossary 1/21/05 2:26 PM Page 434

Page 452: Jabber Developer’s Handbook [Sams 2004]

BXML Basics

The Jabber protocols are defined in terms of messages in Extensible Markup Language(XML) format, so although it’s not important to be fluent in all of XML’s features, it’sdifficult to do much Jabber programming without familiarity with the basics.Thisappendix covers all you need to know to understand the XML used in the Jabber proto-cols.

NOTEThe XML specification standard is maintained by the World Wide Web Consortium (W3C). The authoritative

specification and lots of good resources are available on their Web site at http://www.w3c.org/XML.

A Simple DocumentXML is a subset of a language called Standard Generalized Markup Language (SGML)and, although it was originally designed for online publishing, it has been adapted tomany uses (including the Jabber protocols).At its core, XML is very simple, making iteasy to adapt to these various uses. It is a text-based rather than binary format, whichmakes it easy to develop and debug just by reading the document and editing it withtext editors. Here’s a simple example document:

1: <?XML version=”1.0”?>

2: <message>

3: <to>Bob Marshall</to>

4: <from>Bob Marshall</from>

5: <receiptRequested/>

6: <body>

7: Here’s the info you requested.

8: <info>

9: Don’t take any wooden nickels.

10: </info>

11: </body>

12: </message >

17 0672325365 AppB 1/21/05 2:26 PM Page 435

Page 453: Jabber Developer’s Handbook [Sams 2004]

436 Appendix B XML Basics

This will look familiar if you’ve ever worked with HTML.There are tags, enclosed in<>, that delimit elements of the document and can be nested to form a hierarchical doc-ument.The points of interest here include the following:

n Line 1 is a processing instruction, which tells software parsing the document whattype of document it is.This is essentially “boilerplate” material included in anyXML document.

n Line 2 begins an element delimited by the <message> tag at its beginning and the</message> tag at its end at line 12. Note that the leading “/” character identifiesthe end tag.An XML document always has an element that encloses the wholedocument like this, called the root element.

n Line 3 shows an element enclosed within the <message> element.The way Jabberuses XML, the line breaks between elements have no meaning, so the documenthere is formatted to make it easier to read. In practice, there are often no linebreaks between elements.

n Line 5 shows an element with no content: <receiptRequested/>.When an ele-ment has no content, this abbreviated tag form can be identified by the trailing “/”character.This form means the same thing as <receiptRequested></receiptRequested>.

n Line 6 begins an element (<body>) that has another element enclosed within it(<info>).The capability to enclose elements within other elements is used exten-sively in the Jabber protocols to embed message elements in messages.

Tag AttributesAttributes are name=value pairs that can appear in opening tags.They qualify the ele-ment and are used extensively in the jabberd configuration file and the Jabber protocols.Here’s an element that contains two attributes: type=’result’ and id=’JCOM_25’.

<iq type=’result’ id=’JCOM_25’/>

Attribute values must always be enclosed in single or double quotes.

CommentsYou can include comments in an XML document by enclosing the comment textbetween <!-- and -->.

<!-- An example comment “here” -->

A comment can contain any characters except two consecutive dashes (“--”).The jab-berd configuration file, jabber.xml, contains many useful comments describing themeaning of the configuration elements.You might also find it useful to use commentdelimiters to temporarily remove configuration data, as in this example in which the SSLconnection ports are disabled:

17 0672325365 AppB 1/21/05 2:26 PM Page 436

Page 454: Jabber Developer’s Handbook [Sams 2004]

437A Simple Document

<service id=”c2s”>

<load>

<pthsock_client>./libs/pthsock_client.dll</pthsock_client>

</load>

<pthcsock xmlns=’jabber:config:pth-csock’>

<authtime/>

<ip port=”5222”/>

<!--

<ssl port=’5223’>127.0.0.1</ssl>

<ssl port=’5224’>192.168.1.100</ssl>

-->

</pthcsock>

</service>

XML NamespacesIt’s likely that with people all over to world building XML documents, there will beconflicts over element and attribute names in which a particular name means one thingin one context and something else in another. Or similarly, a document may contain amixture of content that is for use by different applications. XML namespaces weredesigned to help avoid these problems.

A namespace is identified by a Uniform Resource Identifier (URI) and is a collectionof names that are used for elements and attributes in a particular context.Although ingeneral XML namespace URIs refer to Web addresses (such as http://www.w3.org/TR/REC-html40, for example), Jabber’s XML namespaces are simply colon-separatedidentifiers.They are used to take XML elements as specific types of messages in theJabber protocol.The special attribute name xmlns is used to declare an XML namespace.Here’s an example of a message using the jabber:iq:register namespace that requeststhe registration of a new Jabber user:

<iq type=”set” id=”JCOM_23”>

<query xmlns=”jabber:iq:register”>

<username>user1</username>

<password>password</password>

<resource>Work</resource>

<email>[email protected]</email>

</query>

</iq>

The xmlns attribute in the <query> tag tells the server that this query should be inter-preted as a registration message.There can be more than one xmlns attribute in an ele-ment, but that occurs rarely in the Jabber protocol.

17 0672325365 AppB 1/21/05 2:26 PM Page 437

Page 455: Jabber Developer’s Handbook [Sams 2004]

438 Appendix B XML Basics

XML StreamsXML is a format for documents, but Jabber is a streaming protocol.That is, messages areexchanged in both directions between client and server over time. XML streams is theapproach that the Jabber protocol uses to exchange well-formed XML interactively.

The entire interaction between the client and the server is a single XML documentexchanged piecemeal over time. Jabber messages are elements within that document.That is, from the time the client connects to the server to the time it disconnects, theclient sends one XML document. Likewise, from the time the client connects until it dis-connects, the server sends the client one XML document. Elements of these two docu-ments are exchanged back and forth as Jabber messages.

An XML document always begins with the XML processing instruction and the rootelement tag.When a Jabber client connects to a server, it sends this data to start the con-versation:

<?xml version=’1.0’?>

<stream:stream to=”my-jabber” xmlns=”jabber:client”

xmlns:stream=”http://etherx.jabber.org/streams”>

The two XML namespaces are significant.n xmlns=”jabber:client” identifies this connection as a Jabber client.n xmlns:stream=”http://etherx.jabber.org/streams” identifies this connection

as a Jabber message stream.

So the root element is always named stream:stream in a Jabber protocol stream.Thisopening tag is called the stream header.The server responds with the start of its own doc-ument:

<?xml version=’1.0’?>

<stream:stream xmlns:stream=’http://etherx.jabber.org/streams’

id=’3DE29D5E’ xmlns=’jabber:client’ from=’my-jabber’>

The server includes the same namespaces as the client.

NOTEThe namespace jabber:client is used for client-to-server connections. Other namespaces are used for

server-to-server and server-to-component streams.

After the stream headers are exchanged, either party can send message elements to theother. Either the client or the server can end the session by closing the root elementwith a closing </stream:stream> tag.This tells the other party that the session is over.

17 0672325365 AppB 1/21/05 2:26 PM Page 438

Page 456: Jabber Developer’s Handbook [Sams 2004]

CResources

THE JABBER COMMUNITY IS VIBRANT AND expanding, and there are a number ofresources you can leverage to get code libraries, answers to protocol or implementationquestions, or contribute your own code to the open source efforts. Because of thedynamic nature of any open source program, some of the resources we have listed in thisappendix will have changed by the time you read this book, some will have disappeared,and a whole new group of sites will have sprung up.We have listed resources that arecorrect at the time of publication, but of course your mileage may vary.

Jabber ServersSeveral Jabber servers are available under various license terms.All the examples in thisbook were written and tested with the open source reference implementation server,version 1.4.2, from http://jabberd.jabberstudio.org. Jabberstudio also hosts someother open source Jabber servers written for various special purposes.

Commercial servers are available from a few companies, including Jabber.com(http://www.jabber.com), Rhombus (http://www.rhombusim.com),Antepo (http://www.antepo.com), i3Connect (http://www.i3connect.com), and Tipic (http://www.tipic.com).

Jabber ProtocolReference documentation on the Jabber protocol itself is available at http://www.jabber.org/protocol.

Jabber ClientsThe examples in this book use the open source WinJab client and its follow-on, Exodus.Winjab is available at http://sourceforge.net/projects/winjab.Although its authorsdescribe it as “deprecated” in favor of Exodus, at this writing WinJab is a more complete

18 0672325365 AppC 1/21/05 2:27 PM Page 439

Page 457: Jabber Developer’s Handbook [Sams 2004]

440 Resources

and reliable client. Exodus is available at http://exodus.jabberstudio.org.A list ofopen and proprietary clients is at http://www.jabber.org/user/clientlist.php.

Jabber LibrariesAs this book was being written, http://www.jabber.org/developer/librarylist.phpwas a repository for code libraries, ranging from C++ clients and servers, to WinCEclients, to Macromedia Flash clients.

Macromedia Flash The Flash Messaging Libraries at http://fml.jabberstudio.org/ are a bundling ofFlash MovieClips and ActionScript APIs that enable you to create scalable vector graphicclients based on Flash.The Macromedia developer environments (for exampleMacromedia Flash Studio) use proprietary technology, but the really interesting thinghere is that because there are Flash players for just about every platform under the sun,and especially for Windows CE devices, you may be able to write a single client imple-mentation that will run well everywhere.Also, because ActionScript is just another namefor ECMAScript (formerly JavaScript), this is the only implementation in JavaScript thatwe know of.You might (potentially) decouple the Flash UI and get a niceRhino/ECMAScript handler library.

JabberBeansJabberBeans, at www.jabberstudio.org/projects/jabberbeans/project/view.php, isa full-featured Java-based library that we have used extensively in examples for this book.If you are a Java adherent (as are we), you will find JabberBeans to be pretty robust andfull featured.That being said, it is fairly low level and mechanistic—you have to dothings in a pretty procedural way: connect, then advertise your presence, then get yourroster, then handle messages and queries through event callbacks. JabberBeans comeswith useful and instructive examples and is available open source under an LGPL license.

AlicebotsIn Chapter 8,“Jabber and Conversational Software Agents,” we showed an approach tocreating intelligent responders as Jabber clients.We used the software package availablefrom http://www.alicebot.org/.The Alice software suite is rapidly evolving, but inter-esting to explore.

JabberPyThe jabber.py library, available at http://jabberpy.sourceforge.net, is a relativelycomplete Python library for Jabber. jabber.py deals with the XML parsing and socketcode. It also offers concrete classes for creating and managing clients and all three

18 0672325365 AppC 1/21/05 2:27 PM Page 440

Page 458: Jabber Developer’s Handbook [Sams 2004]

441Miscellany

message types, and for handling rosters.As we’ve shown in examples in this book,jabber.py works well with GUIs, provided you use the Python threading Queue libraryso that there’s no conflict between the jabber.py callback handlers and the GUI eventloop (in wzPython or PythonCard, for example). jabber.py implements much (but notall) of the Jabber protocol—certainly enough to make reasonable applications.

jabber.py requires at least Python 2.0 and the XML expat parser module (includedin the standard Python distrubution). It has been updated to support new-style Pythonclasses, and the applications shown in the text were written with either Python 2.2 orPython 2.3.The original development work was done on Linux, so certain examplecode uses Linux-only capabilities (such as the select socket call). Even so, the examplesshown in the book were tested on Win32. jabber.py is available open source under anLGPL license.

MiscellanySome additional fascinating efforts were current as of publication. Several creative clients(for example, Lluna, which uses animated avatars, and BuddySpace, which has an inter-esting side application for mapping users and associating their IM presence with a physi-cal presence) are available at www.jabber.org.

IBM has a Jabber client written in their Sash development environment that usesHTML, XML, and JavaScript to write programs that look like Windows programs(http://sash.alphaworks.ibm.com/download/sashjab/).

There’s actually an XML editor called the XML Cooktop (http://www.xmlcooktop.com/) that has a built-in Jabber client to do collaboration.And let’s not forget EMACS;there seems to be a plugin for just about every purpose in EMACS, and Jabber is noexception. Check out http://savannah.nongnu.org/projects/smyrno/.

In Chapter 7,“Jabber and Web Services,” we mentioned Jabber-RPC, which is aneffort to port XML-RPC requests over Jabber (seewww.pipetree.com/jabber/jrpc.html).

18 0672325365 AppC 1/21/05 2:27 PM Page 441

Page 459: Jabber Developer’s Handbook [Sams 2004]

18 0672325365 AppC 1/21/05 2:27 PM Page 442

Page 460: Jabber Developer’s Handbook [Sams 2004]

Symbols<> (angle brackets), 436

<!— —> tag, 436-437

3-tier architecture, 274-275

302-510 error codes, 81-82

3270 glass terminals, 274

AActiveState ActivePython, 330

addDiscoveryListener() method, 388

addressing, 23-24

administrationApache monitoring client, 350-363

changedPresence() method, 362checkData() method, 361code listing, 352, 355-359getStatusData() method, 360httpd.conf configuration, 350-351main processing loop, 359receivedData() method, 362-363sample messages, 352sendData() method, 361startJabber() method, 359-360Web server status page, 351-352

distributed control, 341-342group messaging, 343one-on-one messaging, 342-343system control script, 344-350

inventory management service,198-200

import statements, 208init() method, 213InvComponent.py, 200-207inventoryClient.py, 207<iq> messages, 208-210iqCB() method, 209isAvailable() method, 213jabber.xml configuration, 211jabberd connection, 212-213messageCB() method, 211pickled client list, 211-212process() method, 214registration, 214, 221rssAccount.py, 224rssClient.py, 223rssComponent.py, 216-221setOnline() method, 213setShow() method, 213setStatus() method, 213

system event monitoring, 327system logger service, 329-337Unix syslog file, 328Windows Event Viewer, 328

version management<iq> packets, 338<name> tag, 338<os> tag, 338requesting version information,

338-339

Index

19 0672325365 Index 1/21/05 2:27 PM Page 443

Page 461: Jabber Developer’s Handbook [Sams 2004]

444 administration

responding to version requests,339-341

<version> tag, 338administrative users, 58-59

advertisementsdiscovering, 392-393peer advertisements, 366-368talk pipe advertisements, 379-380

agentsbrowsable agents, 142-147querying for, 134-136

Agents property (JabberClient class),400

AIM (AOL Instant Messenger) gate-way

browsing, 148-149configuration, 148message translation, 150-153namespaces, 149presence information, 150user registration, 149-150

AIML (Artificial IntelligenceMarkup Language)

<category> tag, 306ECMAScript, 312-316<javascript> tag, 311<pattern> tag, 306<random> tag, 308-309sample question-and-answer pair, 307<system> tag, 311<template> tag, 306<that> tag, 311<think> tag, 308<topic> tag, 306wild cards, 310

AIML folder, 302-303

<alias> tag, 49

Alicebot, 299-300AIML (Artificial Intelligence Markup

Language)<category> tag, 306ECMAScript, 312-316<javascript> tag, 311<pattern> tag, 306<random> tag, 308-309sample question-and-answer pair, 307<system> tag, 311<template> tag, 306<that> tag, 311<think> tag, 308<topic> tag, 306wild cards, 310

AIML folder, 302-303Alice-Jabber architecture, 316-318Alicebot Web site, 440AliceJabber class

code listing, 318-321getResponse() method, 322receivedPacket() method, 321-323

configuration files, 303-305ConnectionBean object, 323defined, 300-301design, 301downloading, 302goals, 300lib folder, 303listener registry, 324running, 324-325Web sites, 301why it works, 301

AliceJabber classcode listing, 318-321getResponse() method, 322receivedPacket() method, 321-323

aliceserver.jar file, 303

<allow> tag, 47-48

19 0672325365 Index 1/21/05 2:27 PM Page 444

Page 462: Jabber Developer’s Handbook [Sams 2004]

445arbitrary data

alpha.xml file, 29-30

angle brackets (<>), 436

AOL Instant Messenger. See AIMgateway

Apache serverApache monitoring client, 350-363

changedPresence() method, 362checkData() method, 361code listing, 352, 355-359getStatusData() method, 360http.conf configuration, 350-351main processing loop, 359receivedData() method, 362-363sample messages, 352sendData() method, 361startJabber() method, 359-360Web server status page, 351-352

Web site, 350appendItem() method, 191

Apple Rendezvous, 370

applicationsApache monitoring client, 350-363

changedPresence() method, 362checkData() method, 361code listing, 352, 355-359getStatusData() method, 360http.conf configuration, 350-351main processing loop, 359receivedData() method, 362-363sample messages, 352sendData() method, 361startJabber() method, 359-360Web server status page, 351-352

conversational applications, 7cross-language chat client

bind command, 418buttonPress() method, 418

code listing, 415-417initial chat window, 414-415jid variable, 417

iksemel application, 408-409code listing, 409-411<iq> packets, 412-413main() method, 411my data structure, 411packetRecv() method, 412

ImageViewer application, 99-100Python version, 109-122Ruby version, 100, 103-109

Jabber-Net C# applicationbuilding, 407code listing, 400-402InitializeExcel() method, 404InitializeJabber() method, 404newIQ() method, 404newMessage() method, 404newPresence() method, 404Presence() method, 404UpdateExcel() method, 404UpdatePresence() method, 404

Jabber-Net Visual Basic applicationbuilding, 407code listing, 404-406

traditional applicationscharacteristics, 7-9limitations of, 9-11

user creation scriptsJava, 25-30Python, 30-32

Applied Cryptography, 258

arbitrary data, sending, 99-100Python application

callback handlers, 119ConnectToJabber() method, 119

How can we make this index more useful? Email us at [email protected]

19 0672325365 Index 1/21/05 2:27 PM Page 445

Page 463: Jabber Developer’s Handbook [Sams 2004]

446 arbitrary data

Jabber library capabilities, 119JabberHandler class, 118jabberHandler.py, 115-117jabberLogin.py, 114messageCB() method, 121pictureViewer.py, 110-114presenceCB() method, 121Process() method, 118sendToRoster() method, 120

Ruby application, 100Bitmapper.rb, 101-106connectToJabber() method, 108-109disconnectFromJabber() method, 109ImageWindow class, 106-107sendJabberImage() method, 107-108

architecture of Jabber, 123asynchronous messaging, 19-20browsable agents, 142-147built-in services, 11-13c2s service, 123client-based services, 17-19client/server architecture, 14-17,

274-275client initialization

authentication, 130-134client/server connections, 130fetching roster, 136-138presence information, 138-141querying for agents, 134-136

conference service, 124decentralization, 21-22dnsrv service, 124extensibility, 20-21Internet addressing, 23intranet addressing, 24instant messaging gateways

browsing, 148-149configuration, 148message translation, 150-153

namespaces, 149presence information, 150user registration, 149-150

JSM (Jabber Session Manager), 123JUD (Jabber User Directory), 124logger services, 124messaging

client-to-client messages, 125-127remote messaging, 127-129

open source licensing, 19, 33presence information, 23real-time messaging, 22-23security, 22servers/glass terminals, 273-274service discovery, 13-14s2s service, 124TCP/IP, 22three-tier architecture, 275xdb (XML database), 124XML, 22Web architectures, 275-276

Artificial Intelligence MarkupLanguage. See AIML

asynchronous messaging, 19-20

attributes, 436

authenticate() method, 234-238, 242

authenticationAuthBase class, 234-236authenticate() method, 234-236authentication modules, 233clients, 130-134custom authentication

authentication service connection, 246authorization packets, 247-249code listing, 249-254disableStreamHeader() method, 254<error> tag, 257isValid() method, 256jabber.cml file, 245-246

19 0672325365 Index 1/21/05 2:27 PM Page 446

Page 464: Jabber Developer’s Handbook [Sams 2004]

447buttonPress() method

<jsm> tag, 246log() method, 256login() method, 256receivedPacket() method, 254replyWithError() method, 255-257replyWithLogin() method, 255setup() method, 254

digest authentication, 238-242authenticate() method, 242code listing, 241-242example, 239-240SHA-1 algorithm, 239

<iq> get requests, 234plain authentication

authenticate() method, 238code listing, 237-238error packets, 237<iq> packets, 236mod_auth_plain module, 238

server-to-server connection authenti-cation, 265-268

zero-knowledge authentication,242-245

code listing, 244<iq> packets, 243mod_auth_Ok module, 242

authorization packets, 247-249

<authtime> tag, 49

auth_result method, 420

AutoAgents property (JabberClientclass), 400

AutoLogin property (JabberClientclass), 400

automatic client registration, dis-abling, 231-232

AutoPresence property (JabberClientclass), 400

AutoRoster property (JabberClientclass), 400

available type attribute (<presence>tag), 88

away value (presence), 89

B-B option (jabberd), 40

base 64 data type, 283

beans, ConnectionBean, 323

bind command, 418

Bitmapper.rb file, 101-106

<body> tag, 56, 436jabber:x:event attachments, 93-94XML CDATA, 94-95

books, Applied Cryptography, 258

Boolean data type, 283

<bots> tag, 304

browsable agents, 142-147

browse requests, 169-170

<browse> tag, 13, 42, 144

browsingAIM (AOL Instant Messenger)

Transport, 148-149browse requests, 169-170

handleBrowse() method, 169-170listDatabases() method, 171listFields() method, 173listTables() method, 172

list of columns, 173list of databases, 171list of tables, 172services, 60

build() method, 192

built-in serviceslibrary modules, 11STDIO, 13TCP/IP sockets, 11-12

buttonPress() method, 418

How can we make this index more useful? Email us at [email protected]

19 0672325365 Index 1/21/05 2:27 PM Page 447

Page 465: Jabber Developer’s Handbook [Sams 2004]

448 -c option

C-c option (jabberd), 40

C# Jabber-Net sample applicationbuilding, 407code listing, 400-402InitializeExcel() method, 404InitializeJabber() method, 404newIQ() method, 404newMessage() method, 404newPresence() method, 404Presence() method, 404UpdateExcel() method, 404UpdatePresence() method, 404

C/C++iksemel

downloading, 408installing, 408sample application, 408-413

JECL (Jabber External ComponentLibraries), 408

sending messages from, 409-413c2s (client-to-server) service, 48-49,123

<category> tag, 306

CDATA, 94-95

cert.pem file, 264

Certificate message (SSL), 258

certificates, 258

changedPresence() method, 362

chatAlicebot, 299-300

AIML (Artificial Intelligence MarkupLanguage), 306-316

AIML folder, 302-303Alice-Jabber architecture, 316-318Alice Java code tree, 317-318Alicebot Web site, 440AliceJabber class, 318-323

configuration files, 303-305ConnectionBean object, 323defined, 300-301design, 301downloading, 302goals, 300lib folder, 303listener registry, 324running, 324-325Web sites, 301why it works, 301

conference service, 63-66, 124cross-language example

bind command, 418buttonPress method, 418code listing, 415-417initial chat window, 414-415jid variable, 417

chat type attribute (<message> tag),93

chat value (presence), 89

chatterbot-based applications(Alicebot), 299-300

AIML (Artificial Intelligence MarkupLanguage)

<category> tag, 306ECMAScript, 312-316<javascript> tag, 311<pattern> tag, 306<random> tag, 308-309sample question-and-answer pair, 307<system> tag, 311<template> tag, 306<that> tag, 311<think> tag, 308<topic> tag, 306wild cards, 310

AIML folder, 302-303

19 0672325365 Index 1/21/05 2:27 PM Page 448

Page 466: Jabber Developer’s Handbook [Sams 2004]

449clients

Alice-Jabber architecture, 316-318Alice Java code tree, 317-318Alicebot Web site, 440AliceJabber class

code listing, 318-321getResponse() method, 322receivedPacket() method, 321-323

configuration files, 303-305ConnectionBean object, 323defined, 300-301design, 301downloading, 302goals, 300lib folder, 303listener registry, 324running, 324-325Web sites, 301why it works, 301

checkData() method, 361

checkPendingMessages() method,393-394

classesAliceJabber

code listing, 318-321getResponse() method, 322receivedPacket() method, 321-323

AuthBase, 234-236ConnectionBean, 230ConnectionBeanSSL, 263DiscoveryService, 376ExecThread, 347-348ImageWindow, 106-107InfoQueryBuilder, 28JabberClient, 399-400JabberHandler, 118Message, 376NewUser

creating in Java, 27creating in Python, 31

PeerGroup, 376PeerGroupFactory, 376PipeService, 376ReaderThread, 335-336ReportData, 189-190ReportDataBuilder, 191-192ReportDataHandler, 192-193XDBBuilder, 194

client-based services, 17-19

client-to-server (c2s) service, 48-49,123

client/server architecture, 14-17,274-275

ClientChangeCipherSpec message(SSL), 259

ClientFinished message (SSL), 259

ClientHello message (SSL), 258

ClientKeyExchange message (SSL),259

clients, 69-70. See also usersApache monitoring client, 350-363

changedPresence() method, 362checkData() method, 361code listing, 352, 355-359getStatusData() method, 360httpd.conf configuration, 350-351main processing loop, 359receivedData() method, 362-363sample messages, 352sendData() method, 361startJabber() method, 359-360Web server status page, 351-352

authentication, 130-134AuthBase class, 234, 236authenticate() method, 234, 236authentication modules, 233custom authentication, 245-257digest authentication, 238-242

How can we make this index more useful? Email us at [email protected]

19 0672325365 Index 1/21/05 2:27 PM Page 449

Page 467: Jabber Developer’s Handbook [Sams 2004]

450 clients

<iq> get requests, 234plain authentication, 236-238zero-knowledge authentication,

242-245c2s service, 48-49, 123client-based services, 17-19client/server architecture, 14-17,

274-275connecting to Jabber server, 130conversational streams, 71-72cross-language chat client

bind command, 418buttonPress method, 418code listing, 415-417initial chat window, 414-415jid variable, 417

defined, 70error codes, 81-82<error> tag, 90initialization

authentication, 130-134client/server connections, 130fetching roster, 136-138presence information, 138-141querying for agents, 134-136

inventory management serviceInvComponent.py, 200-207rssAccount.py, 224rssClient.py, 223rssComponent.py, 216-221

<iq> tag, 78from attribute, 79id attribute, 79namespaces, 82-83to attribute, 79type attribute, 79-82

Jabber-to-JXTA bridgeaddDiscoveryListener() method, 388advertisement discovery, 392-393

checkPendingMessages() method,393-394

client initialization, 389-390code library initialization, 387-388code listing, 380-387discover message, 376-377discoveryEvent() method, 388,

392-393flushAdvertisements() method, 389getLocalAdvertisements() method,

389-391getPipeService() method, 389getRemoteAdvertisements() method,

388, 391groups message, 376main() method, 387messages, receiving, 396messages, sending, 393-395newNetPeerGroup() method, 388peer enumeration, 391-392peers message, 376pipeMsgEvent() method, 396receivedPacket() method, 390-392sendAndWait() method, 390sendToAll() method, 393-394sendToPipe() method, 394-395startJabber() method, 389-390startJxta() method, 387-388talk command, 378-379talk pipe advertisement, 379-380

JXTA initialization, 389-390<message> tag

<body> subelement, 93-95<error> subelement, 96from attribute, 92id attribute, 92<subject> subelement, 95<thread> subelement, 91, 96

19 0672325365 Index 1/21/05 2:27 PM Page 450

Page 468: Jabber Developer’s Handbook [Sams 2004]

451code listings

to attribute, 92type attribute, 92-93<x> subelement, 96-98

multiple clients, 71-72namespaces, 83

jabber:x:autoupdate, 97jabber:x:delay, 97jabber:x:encrypted, 97jabber:x:oob, 98jabber:x:roster, 98

online resources, 439presence information, 89, 138-141<presence> tag, 84-89<priority> tag, 90querying for agents, 134-136reallySimpleXMLRPCInvoker.py,

278-279registration, 57-58, 226

disabling automatic registration,231-232

inventory management service, 214,221

<iq> result packets, 227new user registration example,

227-231registration data, 232registration requests, 226-227reports service, 186-188, 193-194

session mechanics, 70sharing pictures between, 99-100

Python application, 109-122Ruby application, 100, 103-109

<show> tag, 89simple Telnet script, 73-77

<iq> messages, 74-75<message> messages, 76-77<presence> messages, 76

<status> tag, 89<stream:stream> tag, 78

Tkabber, 418user rosters, 136-138welcome messages, 58

code listingsAlicebot application

AliceJabber class, 318-321ECMAScript, 314-315

Apache monitoring client, 352-359AuthBase class, 235-236client registration, 227-229cross-language application, 416-417custom authentication service,

249-254database service

browse requests, 169connecting to server, 166listing databases, 171search instructions, 175-176search requests, 174-175searching databases, 177-178source code, 158-165

digest authentication, 241-242iksemel application, 409-411inventory management service

InvComponent.py, 201-207inventoryClient.py, 207rssAccount.py, 224rssClient.py, 223rssComponent.py, 216-221

Jabber-NetC# sample application, 400-402Visual Basic sample application,

404-406jabber.xml file

c2s configuration, 48dnserv configuration, 51elogger configuration, 52io allow/deny settings, 47

How can we make this index more useful? Email us at [email protected]

19 0672325365 Index 1/21/05 2:27 PM Page 451

Page 469: Jabber Developer’s Handbook [Sams 2004]

452 code listings

JSM (Java Session Manager) configura-tion, 53-55

rlogger configuration, 52s2s configuration, 49-51xdb configuration, 44-46

JabberPRCPeerA.py invoker, 288JabberPRCPeerB.py service provider,

289-291JabberPRCPeerC.py service provider,

293-294JXTA peer advertisement, 367-368JXTA shell commands, 373-374JXTA-Jabber client

advertisement discovery, 393client initialization, 390code library initialization, 388messages, receiving, 396messages, sending, 393-395peer enumeration, 391source code, 380-387

method call XML encoding, 281method response XML encoding, 282NewUser.java, 25-26newUser.py, 30plain authentication, 237-238Python ImageViewer application

jabberHandler.py, 115-117jabberLogin.py, 114pictureViewer.py, 110-114

reallySimpleXMLRPCInvokee.py, 280reallySimpleXMLRPCInvoker.py, 278reports service

client registration, 194generateReports() method, 196-197handleSearchResult() method, 197ReportData class, 190ReportData handler configuration, 193ReportDataBuilder class, 191ReportDataHandler class, 192-193source code, 180-186

rssComponent.py, 216Ruby ImageView

Bitmapper.rb, 101-106connectToJabber() method, 108disconnectFromJabber() method, 109ImageWindow class, 106-107sendJabberImage() method, 107-108

system control script, 344-347system logger service, 330-335version requests, responding to, 340WSDL (Web Services Description

Language), 285XML-RPC wrapped for Jabber trans-

port, 295zero-knowledge authentication, 244

columns, listing, 173

command-line options (jabberd), 40

commandsbind, 418configure, 36-37, 260jabberd, 40JXTA commands, 372-375

list of, 373-374man, 372peers, 374talk, 375, 378-379

make, 37-38, 260make test, 38netstat, 44, 263rpm, 260

comments, 436-437

components, 18

Composing value (jabber:x:eventsattachments), 94

conference service, 63-66, 124

configurationAlicebot, 303-305c2s service, 48-49conference service, 63-66

19 0672325365 Index 1/21/05 2:27 PM Page 452

Page 470: Jabber Developer’s Handbook [Sams 2004]

453controlling sets of computers

database services, 157-158dnsrv service, 51elogger service, 52-53io service, 46

<allow> tag, 47-48<deny> tag, 47-48<karma> tag, 46-47<rate> tag, 46<ssl> tag, 48

Jabber serverAIM (AOL Instant Messenger) gate-

way, 148command-line options, 40jabber.xml file, 40-42

jabber.xml file, 40-42<browse> tag, 42c2s service configuration, 48-49conference service configuration, 63-66custom authentication, 245-246dnsrv service configuration, 51<host> tag, 41inventory management service, 211io service configuration, 46-48<jabberd:cmdline> tag, 41JSM (Jabber Session Manager) configu-

ration, 53-61JUD (Jabber User Directory) configura-

tion, 61-63rlogger configuration, 51-53s2s service configuration, 49-51<service> tag, 40<update> tag, 42<vcard2jud> tag, 42xdb configuration, 44-46

JSM (Jabber Session Manager)client registration, 57-58client welcome message, 58components, 60-61

default configuration, 53-55filters, 56-57server administration, 58-59server vCard, 57service browsing, 60updates, 59vCard synchronization, 59

JUD (Jabber User Directory), 61-63JXTA, 371rlogger service, 51-52s2s service, 49-51

default configuration, 49-50<dialback> tag, 50-51<idletimeout> tag, 50<ip> tag, 50<maxhosts> tag, 50<queuetimeout> tag, 50

SSL (Secure Sockets Layer), 48,260-265

WinJab, 66-68xdb (XML database)

custom configuration, 45-46default configuration, 44-45

configure command, 36-37, 260

Connect() method, 400

connectingauthentication services, 246database service to server, 166-167to Jabber server, 130

ConnectionBean class, 230, 323

ConnectionBeanSSL class, 263

connectToJabber() method, 108-109,119, 419, 422-425

<continue> tag, 57

controlling sets of computers,341-342

group messaging, 343one-on-one messaging, 342-343system control script, 344-350

How can we make this index more useful? Email us at [email protected]

19 0672325365 Index 1/21/05 2:27 PM Page 453

Page 471: Jabber Developer’s Handbook [Sams 2004]

454 conversational agents

conversational agents (Alicebot),299-300

AIML (Artificial Intelligence MarkupLanguage)

<category> tag, 306ECMAScript, 312-316<javascript> tag, 311<pattern> tag, 306<random> tag, 308-309sample question-and-answer pair, 307<system> tag, 311<template> tag, 306<that> tag, 311<think> tag, 308<topic> tag, 306wild cards, 310

AIML folder, 302-303Alice-Jabber architecture, 316-318Alice Java code tree, 317-318Alicebot Web site, 440AliceJabber class

code listing, 318-321getResponse() method, 322receivedPacket() method, 321-323

configuration files, 303-305ConnectionBean object, 323defined, 300-301design, 301downloading, 302goals, 300lib folder, 303listener registry, 324running, 324-325Web sites, 301why it works, 301

conversational applications, 7

cross-language chat clientbind command, 418buttonPress method, 418

code listing, 415-417initial chat window, 414-415jid variable, 417

cryptographic hashes, 238. See alsodigest authentication

custom authenticationauthentication service connection, 246authorization packets, 247-249code listing, 249-254disableStreamHeader() method, 254<error> tag, 257isValid() method, 256jabber.cml file, 245-246<jsm> tag, 246log() method, 256login() method, 256receivedPacket() method, 254replyWithError() method, 255-257replyWithLogin() method, 255setup() method, 254

custom packet handler, 188-189ReportData class, 189-190ReportData handler configuration,

193ReportDataBuilder class, 191-192ReportDataHandler class, 192-193

Cygwin Unix tools, 35

D-D option (jabberd), 40

data types, 282-283

database service, 156browsing databases

browse requests, 169-170handleBrowse() method, 169-170list of columns, 173list of databases, 171list of tables, 172

19 0672325365 Index 1/21/05 2:27 PM Page 454

Page 472: Jabber Developer’s Handbook [Sams 2004]

455directories

listDatabases() method, 171listFields() method, 173listTables() method, 172

code listing, 158-165configuring, 157-158connecting to server, 166-167JDBC (Java Database Connectivity),

167-168searching databases

getColumns() method, 176getSearchInstructions() method,

175-176handleSearch() method, 174-175search form, 176-177search instructions, 175-176search requests, 174search result packets, 178-179search() method, 177-178sendError() method, 175

databasesbrowsing

browse requests, 169-170handleBrowse() method, 169-170list of columns, 173list of databases, 171list of tables, 172listDatabases() method, 171listFields() method, 173listTables() method, 172

database service, 156browsing databases, 169-173code listing, 158-165configuring, 157-158connecting to server, 166-167JDBC (Java Database Connectivity),

167-168searching databases, 174-179

JDBC (Java Database Connectivity),167-168

getCatalogs() method, 168getConnection() method, 167-168getTables() method, 168next() method, 168ResultSet object, 168

listing, 171searching

code listing, 177-179getColumns() method, 176getSearchInstructions() method, 175handleSearch() method, 174-175search form, 176-177search instructions, 175-176search requests, 174-175search result packets, 178-179search() method, 177-178sendError() method, 175

xdb (XML database), 44-46, 124date data type, 283

dec attribute (<karma> tag), 47

decentralization, 21-22

Delivered value (jabber:x:eventsattachments), 93

<deny> tag, 47-48

dialback authentication, 265-268

<dialback> tag, 50-51

digest authentication, 238-242authenticate() method, 242code listing, 241-242example, 239-240SHA-1 algorithm, 239

<digest> tag, 241

directoriesjabber-1.4.2, 36JUD (Jabber User Directory), 61-63,

124

How can we make this index more useful? Email us at [email protected]

19 0672325365 Index 1/21/05 2:27 PM Page 455

Page 473: Jabber Developer’s Handbook [Sams 2004]

456 disableStreamHeader() method

disableStreamHeader() method, 254

disabling automatic client registra-tion, 231-232

disconnectedCB() method, 32

disconnectFromJabber() method,109

discoveringJXTA advertisements, 392-393services, 13-14

discovery service, 369

discoveryEvent() method, 388,392-393

DiscoveryService class, 376

<display> tag, 340

Displayed value (jabber:x:eventsattachments), 93

distributed control, 341-342group messaging, 343one-on-one messaging, 342-343system control script

code listing, 344-347ExecThread class, 347-348<iq> packets, 349messageCB() method, 348<nick> tag, 349<presence> packets, 349response_suffix string, 347run() method, 348

dnd value (presence), 89

DNS (Domain Name System). Seednsrv service

dnsrv service, 51, 124

downloadingAlicebot, 302iksemel, 408Jabber server, 35-36JXTA, 370

dumps() method, 279, 289

EEasy Installer (JXTA), 370-371

ECMAScript, 312-316

edge peers (JXTA), 370

elementChars() method, 193

elogger service, 52-53, 124

enabling SSL (Secure SocketsLayer), 260-265

endpoints, 368

enumerating JXTA peers, 391-392

error codes, 81-82

<error> tag, 56, 90, 96, 257

error type attribute<message> tag, 93<presence> tag, 89

error value (<iq> tag), 81

Event Viewer, 328

eventsmonitoring, 327

system logger service, 329-337Unix syslog file, 328Windows Event Viewer, 328

self.wait_handle event, 336Excel spreadsheets, maintainingfrom Jabber

C# applicationbuilding, 407code listing, 400-402InitializeExcel() method, 404InitializeJabber() method, 404newIQ() method, 404newMessage() method, 404newPresence() method, 404Presence() method, 404UpdateExcel() method, 404UpdatePresence() method, 404

19 0672325365 Index 1/21/05 2:27 PM Page 456

Page 474: Jabber Developer’s Handbook [Sams 2004]

457get value

Visual Basic applicationbuilding, 407code listing, 404-406

ExecThread class, 347-348

Exodus Web site, 440

extensibility, 20-21

Extensible Markup Language. SeeXML tags

Ffiles. See also code listings

aliceserver.jar, 303cert.pem, 264global.xdb, 196http.conf, 350-351jabber.xml, 40-42

<browse> tag, 42c2s service configuration, 48-49conference service configuration, 63-66custom authentication, 245-246dnsrv service configuration, 51<host> tag, 41inventory management service, 211io service configuration, 46-48<jabberd:cmdline> tag, 41JSM (Jabber Session Manager) configu-

ration, 53-61JUD (Jabber User Directory), 61-63rlogger configuration, 51-53s2s service configuration, 49-51<service> tag, 40<update> tag, 42<vcard2jud> tag, 42xdb configuration, 44-46

Jabberbeans.jar, 303JabberD-1.4.2.exe, 36js.jar, 303

key.pem, 262mysql.jar, 303org.mortbay.jetty.jar, 303privkey.pem, 262startup.xml (Alicebot), 303-305syslog, 328user1.xml, 232user2.xml, 232xerces.jar, 303

filters (JSM), 56-57

finding services, 13-14

Flash Messaging Libraries, 440

float data type, 282

flushAdvertisements() method, 389

foldersAIML, 302-303lib, 303

<foo> tag, 139

formatReport() method, 197

<forward> tag, 56

from attribute<iq> tag, 79message tag, 92presence tag, 88route tag, 131, 135

<from> tag, 56

Ggateways, instant messaging gate-ways

browsing, 148-149configuration, 148message translation, 150-153namespaces, 149presence information, 150user registration, 149-150

generateReports() method, 196-197

get value (<iq> tag), 80

How can we make this index more useful? Email us at [email protected]

19 0672325365 Index 1/21/05 2:27 PM Page 457

Page 475: Jabber Developer’s Handbook [Sams 2004]

458 getCatalogs() method

getCatalogs() method, 168

getColumns() method, 176

getConnection() method, 167-168

getDiscoveryService() method, 376

getLocalAdvertisement() method,376

getLocalAdvertisements() method,389-391

getPipeService() method, 376, 389

getRemoteAdvertisement() method,376

getRemoteAdvertisements()method, 388, 391

getResponse() method, 322

getSearchInstructions() method, 175

getStatusData() method, 360

getTables() method, 168

giveInstructions() method, 187

glass terminals, 274

global.xdb file, 196

goUnix() method, 337

goWin32() method, 336

graphics, sharing between clients.See ImageViewer application

<group> tag, 56

groupchat type attribute (<message> tag), 93

H-H option (jabberd), 40

handleBrowse() method, 169-170

handleRegistration() method, 188

handlers, KeyboardInterrupt, 210

handleSearch() method, 174-175

handleSearchResult() method, 197

handleXDB() method, 195

handshakes (SSL), 258

headers, stream headers, 438

headline messages, sending from C,409-413

headline type attribute (<message>tag), 93

heartbeat attribute (<karma> tag),46

history of Web servicesclient/server architecture, 274-275servers/glass terminals, 273-274Web architectures, 275

<history> tag, 65

<host> tag, 41

<hosts> tag, 143

http.conf file, 350-351

I-i option (jabberd), 40

id attribute<iq> tag, 79<message> tag, 92<presence> tag, 88<stream> tag, 130

identifiers, URNs (uniform resourcenames), 366

<idletimeout> tag, 50, 268

igCB() method, 32

iksemeldownloading, 408installing, 408sample application, 408-409

code listing, 409-411IQ packets, 412-413main() method, 411my data structure, 411packetRecv() method, 412

images, sharing between clients. SeeImageViewer application

19 0672325365 Index 1/21/05 2:27 PM Page 458

Page 476: Jabber Developer’s Handbook [Sams 2004]

459inventory management service

ImageViewer application, 99-100Python version

callback handlers, 119ConnectToJabber() method, 119Jabber library capabilities, 119JabberHandler class, 118jabberHandler.py, 115-117jabberLogin.py, 114messageCB() method, 121pictureViewer.py, 110-114presenceCB() method, 121Process() method, 118sendToRoster() method, 120

Ruby version, 100Bitmapper.rb, 101-106connectToJabber() method, 108-109disconnectFromJabber() method, 109ImageWindow class, 106-107sendJabberImage() method, 107-108

ImageWindow class, 106-107

inc attribute (<karma> tag), 47

<info> tag, 436

InfoQuery messages, 28

InfoQueryBuilder class, 28

init attribute (<karma> tag), 46

<init> tag, 49

init() method, 213

initCache() method, 195

initializationJabber clients

authentication, 130-134client/server connections, 130fetching roster, 136-138presence information, 138-141querying for agents, 134-136

JXTA-Jabber client, 389-390JXTA libraries, 387-388

InitializeExcel() method, 404

InitializeJabber() method, 404

installationiksemel, 408Jabber server

Linux, 36-39Unix, 36-39Windows, 39

jabber4r, 425JXTA, 370-371Net::Jabber, 422

instant messaginggateways, 148-153

browsing, 148-149configuration, 148message translation, 150-153namespaces, 149presence information, 150user registration, 149-150

WinJab, 66-68<instructions> tag, 144-145, 188

integer data type, 282

Internet addressing, 23

intranet addressing, 24

InvComponent.py, 200-207

inventory management service,198-200

import statements, 208init() method, 213InvComponent.py, 200-207inventoryClient.py, 207<iq> messages, 208-210iqCB() method, 209isAvailable() method, 213jabber.xml configuration, 211jabberd connection, 212-213messageCB() method, 211pickled client list, 211-212

How can we make this index more useful? Email us at [email protected]

19 0672325365 Index 1/21/05 2:27 PM Page 459

Page 477: Jabber Developer’s Handbook [Sams 2004]

460 inventory management service

process() method, 214registration, 214, 221rssAccount.py, 224rssClient.py, 223rssComponent.py, 216-221setOnline() method, 213setShow() method, 213setStatus() method, 213

inventoryClient.py, 207

invokees (XML-RPC), 280

invokers (XML-RPC)JabberRPCPeerA.py invoker, 287-289reallySimpleXMLRPCInvoker.py

client, 278io service configuration, 46-48

<allow> tag, 47-48<deny> tag, 47-48<karma> tag, 46-47<rate> tag, 46<ssl> tag, 48

<io> tag, 262

<ip> tag, 49-50, 262

<iq> tag, 20-21, 78, 133, 338, 349authentication get requests, 234browse requests, 169-170client registration, 227from attribute, 79id attribute, 79inventory management service,

208-210namespaces, 82-83plain authentication, 236-237reports service registration, 187search requests, 174-175simple Telnet example, 74-75to attribute, 79type attribute, 79-82zero-knowledge authentication, 243

iqCB() method, 209

isAvailable() method, 213

isValid() method, 256

<item> tag, 147

Jjabber-1.4.2 directory, 36

Jabber architectureasynchronous messaging, 19-20built-in services

library modules, 11STDIO, 13TCP/IP sockets, 11-12

client-based services, 17-19client/server interaction, 14-17decentralization, 21-22extensibility, 20-21Internet addressing, 23intranet addressing, 24open source licensing, 19, 33presence information, 23real-time messaging, 22-23security, 22service discovery, 13-14TCP/IP, 22XML, 22

Jabber clients. See clients

Jabber External ComponentLibraries (JECL), 408

Jabber, Inc.Web site, 33

Jabber Messages message (SSL), 259

Jabber-Netbuilding applications, 407C# sample application

building, 407code listing, 400-402InitializeExcel() method, 404InitializeJabber() method, 404

19 0672325365 Index 1/21/05 2:27 PM Page 460

Page 478: Jabber Developer’s Handbook [Sams 2004]

461Jabber server

newIQ() method, 404newMessage() method, 404newPresence() method, 404Presence() method, 404UpdateExcel() method, 404UpdatePresence() method, 404

Connect() method, 400JabberClient class, 399-400Visual Basic sample application,

404-407Web site, 399

Jabber-RPC, 441

Jabber server, 123, 155-156. See alsoservices

administration, 58-59Alicebot, 299-300

AIML (Artificial Intelligence MarkupLanguage), 306-316

AIML folder, 302-303Alice-Jabber architecture, 316-318Alice Java code tree, 317-318Alicebot Web site, 440AliceJabber class, 318-323configuration files, 303-305ConnectionBean object, 323defined, 300-301design, 301downloading, 302goals, 300lib folder, 303listener registry, 324running, 324-325Web sites, 301why it works, 301

browsable agents, 142-147client initialization

authentication, 130-134client/server connections, 130

fetching roster, 136-138presence information, 138-141querying for agents, 134-136

client/server interaction, 14-17configuration

AIM (AOL Instant Messenger) gate-way, 148

command-line options, 40jabber.xml file, 40-42multiple virtual Jabber servers, 41

database service, 156browsing databases, 169-173code listing, 158-165configuring, 157-158connecting to server, 166-167JDBC (Java Database Connectivity),

167-168searching databases, 174-179

dnsrv service, 124downloading, 35-36installation

Linux, 36-39Unix, 36-39Windows, 39

instant messaging gateways, 148-153inventory management service,

198-200import statements, 208init() method, 213InvComponent.py, 200-207inventoryClient.py, 207<iq> messages, 208-210iqCB() method, 209isAvailable() method, 213jabber.xml configuration, 211jabberd connection, 212-213messageCB() method, 211pickled client list, 211-212

How can we make this index more useful? Email us at [email protected]

19 0672325365 Index 1/21/05 2:27 PM Page 461

Page 479: Jabber Developer’s Handbook [Sams 2004]

462 Jabber server

process() method, 214registration, 214, 221rssAccount.py, 224rssClient.py, 223rssComponent.py, 216-221setOnline() method, 213setShow() method, 213setStatus() method, 213

messagingclient-to-client messages, 125-127remote messaging, 127-129

online resources, 439reports service, 179-180

code listing, 180-186registration, 186-188, 193-194ReportData class, 189-190ReportData handler configuration, 193ReportDataBuilder class, 191-192ReportDataHandler class, 192-193timed report generation, 196-198XDB custom storage, 193-196

s2s service, 49-51, 124server-to-server connection authenti-

cation, 265-268starting, 43-44stopping, 43troubleshooting, 43-44vCards

configuration, 57synchronization, 59

Jabber session manager. See JSM

Jabber Software Foundation, 33

Jabber Studio Web site, 61, 123, 439

Jabber-to-JXTA bridgeaddDiscoveryListener() method, 388advertisement discovery, 392-393checkPendingMessages() method,

393-394

client initialization, 389-390code library initialization, 387-388code listing, 380-387discover message, 376-377discoveryEvent() method, 388,

392-393flushAdvertisements() method, 389getLocalAdvertisements() method,

389-391getPipeService() method, 389getRemoteAdvertisements() method,

388, 391groups message, 376main() method, 387messages, receiving, 396messages, sending

queued messages, 394sendToPipe() method, 394-395to everyone on roster, 393

newNetPeerGroup() method, 388peer enumeration, 391-392peers message, 376pipeMsgEvent() method, 396receivedPacket() method, 390, 392sendAndWait() method, 390sendToAll() method, 393-394sendToPipe() method, 394-395startJabber() method, 389-390startJxta() method, 387-388talk command, 378-379talk pipe advertisement, 379-380

Jabber User Directory (JUD), 61-63,124

jabber.py library, 440-441

jabber.xml file, 40-42<browse> tag, 42c2s service configuration, 48-49conference service configuration,

63-66custom authentication, 245-246

19 0672325365 Index 1/21/05 2:27 PM Page 462

Page 480: Jabber Developer’s Handbook [Sams 2004]

463JECL

dnsrv service configuration, 51<host> tag, 41inventory management service, 211io service configuration, 46-48<jabberd:cmdline> tag, 41JSM (Jabber Session Manager) config-

urationclient registration, 57-58client welcome messages, 58components, 60-61default configuration, 53-55filters, 56-57server administration, 58-59service browsing, 60updates, 59vCards, 57-59

JUD (Jabber User Directory) configu-ration, 61-63

rlogger configuration, 51-53s2s service configuration, 49-51<service> tag, 40<update> tag, 42<vcard2jud> tag, 42xdb configuration, 44-46

jabber4r, 424-427connectToJabber() method, 425installing, 425messageCB() method, 426rosterCB() method, 427sendMsg() method, 425sendPresence() method, 426subscriptionCB() method, 426-427

JabberBeans, 316, 413-414, 440

Jabberbeans.jar file, 303

JabberClient class, 399-400

JabberD-1.4.2.exe file, 36

jabberd command, 40

jabberd. See Jabber server

<jabberd:cmdline> tag, 41

JabberHandler class, 118

jabberHandler.py file, 115-117

Jabberlib, 418-421auth_result() method, 420connectToJabber() method, 419roster_cmd() method, 420sendMsg() method, 420sendPresence() method, 421

jabberLogin.py file, 114

JabberPy, 414

JabberRPCPeerA.py invoker, 287-289

JabberRPCPeerB.py serviceprovider, 289-292

JabberRPCPeerC.py serviceprovider, 292-295

jabber:iq:register namespace, 187

jabber:iq:search namespace, 173-174

jabber:x:autoupdate namespace, 97

jabber:x:delay namespace, 97

jabber:x:encrypted namespace, 97

jabber:x:event namespace, 93-94

jabber:x:oob namespace, 98

jabber:x:roster namespace, 98

jar files, 303

Java Binding API, 375-376

Java Database Connectivity. SeeJDBC

Java scripts, user creation scriptalpha.xml file, 29-30NewUser class, 27NewUser.bat file, 26NewUser.java code listing, 25-26user registration, 27-28

<javascript> tag, 311

JDBC (Java Database Connectivity),167-168

JECL (Jabber External ComponentLibraries), 408

How can we make this index more useful? Email us at [email protected]

19 0672325365 Index 1/21/05 2:27 PM Page 463

Page 481: Jabber Developer’s Handbook [Sams 2004]

464 jid variable

jid variable, 417

JID() method, 193

js.jar file, 303

JSM (Jabber Session Manager), 41,123

client registration, 57-58client welcome message, 58components, 60-61default configuration, 53-55filters, 56-57server administration, 58-59server vCard, 57service browsing, 60updates, 59vCard synchronization, 59

<jsm> tag, 246

JUD (Jabber User Directory), 61-63,124

JXTA, 365-366advertisements

discovering, 392-393peer advertisements, 366-368talk pipe advertisements, 379-380

classes, 375-376client initialization, 389-390commands

list of, 373-374man, 372peers, 374talk, 375, 378-379

configuration, 371discovery service, 369downloading, 370installation, 370-371Jabber-to-JXTA bridge

addDiscoveryListener() method, 388advertisement discovery, 392-393checkPendingMessages() method,

393-394

client initialization, 389-390code library initialization, 387-388code listing, 380-387discover message, 376-377discoveryEvent() method, 388,

392-393flushAdvertisements() method, 389getLocalAdvertisements() method,

389-391getPipeService() method, 389getRemoteAdvertisements() method,

388, 391groups message, 376main() method, 387messages, receiving, 396messages, sending, 393-395newNetPeerGroup() method, 388peer enumeration, 391-392peers message, 376pipeMsgEvent() method, 396receivedPacket() method, 390-392sendAndWait() method, 390sendToAll() method, 393-394sendToPipe() method, 394-395startJabber() method, 389-390startJxta() method, 387-388talk command, 378-379talk pipe advertisement, 379-380

Java Binding API, 375-376membership service, 369messages, 368

receiving, 396sending, 393-395

modules, 369peer groups, 369peers

edge peers, 370endpoints, 368

19 0672325365 Index 1/21/05 2:27 PM Page 464

Page 482: Jabber Developer’s Handbook [Sams 2004]

465log() method

enumerating, 391-392peer groups, 369proxy peers, 370relay peers, 370rendezvous peers, 370

pipe service, 369propogate pipes, 369resolver service, 369services, 369URNs (uniform resource names), 366Web site, 370

K-L<karma> tag, 46-47

key.pem file, 262

KeyboardInterrupt handler, 210

<learn> tag, 305

lib folder, 303

libraries, 11iksemel

downloading, 408installing, 408sample application, 408-413

Jabber-Net, 399building applications, 407C# sample application, 400-404, 407Connect() method, 400JabberClient class, 399-400Visual Basic sample application,

404-407Web site, 399

jabber.py, 440-441jabber4r, 424-427

connectToJabber() method, 425installing, 425messageCB() method, 426

rosterCB() method, 427sendMsg() method, 425sendPresence() method, 426subscriptionCB() method, 426-427

JabberBeans, 413-414JabberBeans Library, 316Jabberlib, 418-421JabberPy, 414JECL (Jabber External Component

Libraries), 408JXTA libraries, 387-388Net::Jabber, 421-424

connectToJabber method, 422-423installing, 422messageCB method, 423pollJabber method, 423presenceCB method, 424sendMsg method, 423sendPresence method, 424

online resources, 440licensing, open source, 19, 33

Linux, installing Jabber server on,36-39

configure command, 36-37make command, 37-38

listDatabases() method, 171

listen() method, 17

<listeners> tag, 304

listFields() method, 173

listingcolumns, 173databases, 171tables, 172

listTables() method, 172

<load> tag, 45

loads() method, 289

<log> tag, 124

log() method, 256

How can we make this index more useful? Email us at [email protected]

19 0672325365 Index 1/21/05 2:27 PM Page 465

Page 483: Jabber Developer’s Handbook [Sams 2004]

466 logging

logging, 124elogger, 52-53rlogger, 51-52system events, 327

system logger service, 329-337Unix syslog file, 328Windows Event Viewer, 328

login() method, 256

<logtype> tag, 53

MMacromedia Flash MessagingLibraries, 440

main() method, 387iksemel application, 411user creation script, 27

make command, 37-38, 260

make test command, 38

man command, 372

management. See administration

max attribute (<karma> tag), 46

<maxfiles> tag, 45

<maxhosts> tag, 50

<maxrooms> tag, 65

membership service, 369

Message class, 376

<message> tag, 91-92, 125, 436<body> subelement

jabber:x:event attachments, 93-94XML CDATA, 94-95

<error> subelement, 96from attribute, 92id attribute, 92simple Telnet example, 76-77<subject> subelement, 95<thread> subelement, 91, 96to attribute, 92

type attribute, 92-93<x> subelement, 96-98

messageCB() method, 32, 121, 211,348, 423, 426

messaging See also tags (XML)Apache monitoring client, 352asynchronous messaging, 19-20client welcome messages, 58client-to-client messages, 125-127InfoQuery messages, 28instant messaging

browsing, 148-149configuration, 148gateways, 148-153message translation, 150-153namespaces, 149presence information, 150user registration, 149-150

jabber:x:event attachments, 93-94JXTA messages, 368

receiving, 396sending, 393-395

presence messages, 23, 76-77real-time messaging, 22-23remote messaging, 127-129sending

from C, 409-413cross-language example, 414-418iksemel application, 408-413Jabber-Net C# application, 400-404,

407Jabber-Net Visual Basic application,

404-407jabber4r, 424-427JabberBeans, 413-414Jabberlib, 418-421Net::Jabber, 421-424simple Telnet script, 73-77

19 0672325365 Index 1/21/05 2:27 PM Page 466

Page 484: Jabber Developer’s Handbook [Sams 2004]

467methods

sharing pictures between clients,99-100

Python application, 109, 111-122Ruby application, 100, 103-109

SSL (Secure Sockets Layer)Certificate, 258ClientChangeCipherSpec, 259ClientFinished, 259ClientHello, 258ClientKeyExchange, 259Jabber Messages, 259ServerChangeCipherSpec, 259ServerFinished, 259ServerHello, 258ServerHelloDone, 258

WinJab, 66-68, 263-265XML namespaces, 12, 20

<methodCall> tag, 282

methodsaddDiscoveryListener(), 388appendItem(), 191authenticate(), 234-238, 242auth_result(), 420build(), 192buttonPress(), 418changedPresence(), 362checkData(), 361checkPendingMessages(), 393-394Connect(), 400connectToJabber(), 108-109, 119, 419,

422-425disableStreamHeader(), 254disconnectedCB(), 32disconnectFromJabber(), 109discoveryEvent(), 388, 392-393dumps(), 279, 289elementChars(), 193flushAdvertisements(), 389formatReport(), 197

generateReports(), 196-197getCatalogs(), 168getColumns(), 176getConnection(), 167-168getDiscoveryService(), 376getLocalAdvertisement(), 376getLocalAdvertisements(), 389-391getPipeService(), 376, 389getRemoteAdvertisement(), 376getRemoteAdvertisements(), 388, 391getResponse(), 322getSearchInstructions(), 175getStatusData(), 360getTables(), 168giveInstructions(), 187goUnix(), 337goWin32(), 336handleBrowse(), 169-170handleRegistration(), 188handleSearch(), 174-175handleSearchResult(), 197handleXDB(), 195igCB(), 32initCache(), 195InitializeExcel(), 404InitializeJabber(), 404iqCB(), 209isValid(), 256JID(), 193listDatabases(), 171listen(), 17listFields(), 173listTables(), 172loads(), 289log(), 256login(), 256main(), 387

iksemel application, 411user creation script, 27

How can we make this index more useful? Email us at [email protected]

19 0672325365 Index 1/21/05 2:27 PM Page 467

Page 485: Jabber Developer’s Handbook [Sams 2004]

468 methods

messageCB(), 32, 121, 211, 348, 423,426

newAdvertisement(), 376newIQ(), 404newMessage(), 404newNetPeerGroup(), 376, 388newPresence(), 404next(), 168packetRecv(), 412pipeMsgEvent(), 396pollJabber(), 423Presence(), 404presenceCB(), 32, 121, 424Process(), 118, 214public boolean register(), 27ReadEventLog(), 337receivedData(), 362-363receivedPacket(), 230-231, 254,

321-323, 390-392registerNewUser(), 31replyWithError(), 255-257replyWithLogin(), 255ReportTask(), 196rosterCB(), 427roster_cmd(), 420ROT13(), 280, 291-292run(), 336, 348search(), 177-178sendAndWait(), 390sendData(), 361sendError(), 175sendEvent(), 337sendJabberImage(), 107-108sendMsg(), 420, 423-425sendPresence(), 421, 424-426sendToAll(), 393-394sendToPipe(), 394-395set(), 28setup(), 254startJabber(), 359-360, 389-390startJxta(), 387-388

stopHandler(), 193subscriptionCB(), 426-427UpdateExcel(), 404UpdatePresence(), 404

mod_admin module, 60

mod_agents module, 60

mod_announce module, 60

mod_auth_digest module, 60, 233

mod_auth_Ok module, 60, 233, 242

mod_auth_plain module, 60, 233,238

mod_browse module, 60

mod_echo module, 60

mod_filter module, 60

mod_last module, 60

mod_log module, 61

mod_offline module, 60

mod_presence module, 60

mod_register module, 61

mod_roster module, 60

mod_time module, 60

mod_vcard module, 60

mod_version module, 60

mod_xml module, 61

Model-View-Controller architecture,274

modulesJXTA, 369library modules, 11mod_admin, 60mod_agents, 60mod_announce, 60mod_auth_digest, 60, 233mod_auth_Ok, 60, 233, 242mod_auth_plain, 60, 233, 238mod_browse, 60mod_echo, 60mod_filter, 60mod_last, 60

19 0672325365 Index 1/21/05 2:27 PM Page 468

Page 486: Jabber Developer’s Handbook [Sams 2004]

469next() method

mod_log, 61mod_offline, 60mod_presence, 60mod_register, 61mod_roster, 60mod_time, 60mod_vcard, 60mod_version, 60mod_xml, 61

monitoringApache monitoring client, 350-363

changedPresence() method, 362checkData() method, 361code listing, 352, 355-359getStatusData() method, 360http.conf configuration, 350-351main processing loop, 359receivedData() method, 362-363sample messages, 352sendData() method, 361startJabber() method, 359-360Web server status page, 351-352

system events, 327system logger service, 329-337Unix syslog file, 328Windows Event Viewer, 328

multiple clients, serving, 71-72

multiple virtual Jabber servers, 41

multiplexors, 301

my data structure, 411

mysql.jar file, 303

N<name> tag, 338

namespaces, 12, 20, 437AIM (AOL Instant Messenger)

Transport, 149in <iq> tags, 82-83

jabber:iq:register, 187jabber:iq:search, 173-174jabber:x:autoupdate, 97jabber:x:delay, 97jabber:x:encrypted, 97jabber:x:event, 93-94jabber:x:oob, 98jabber:x:roster, 98table of, 83

.NET environment, Jabber-Net, 399building applications, 407C# sample application, 400-404, 407Connect() method, 400JabberClient class, 399-400Visual Basic sample application,

404-407Web site, 399

netstat command, 44, 263

Net::Jabber, 421-424connectToJabber() method, 422-423installing, 422messageCB() method, 423pollJabber() method, 423presenceCB() method, 424sendMsg() method, 423sendPresence() method, 424

newAdvertisement() method, 376

newIQ() method, 404

newMessage() method, 404

newNetPeerGroup() method, 376,388

newPresence() method, 404

NewUser classcreating in Java, 25-27creating in Python, 31

NewUser.bat file, 26

NewUser.java file, 25-26

newUser.py file, 30

next() method, 168

How can we make this index more useful? Email us at [email protected]

19 0672325365 Index 1/21/05 2:27 PM Page 469

Page 487: Jabber Developer’s Handbook [Sams 2004]

470 <nick> tag

<nick> tag, 65, 349

normal value (presence), 89

<notify> tag, 65

ns attribute (<xdb> tag), 126

<ns> tag, 14, 56, 142, 149

numbers, error codes, 81-82

Oobjects

ConnectionBean, 323ResultSet, 168

<offline> tag, 56

Offline value (jabber:x:eventsattachments), 93

online resources. See also Web sitesAlicebot, 440Flash Messaging Libraries, 440Jabber clients, 439Jabber libraries, 440Jabber protocol, 439Jabber servers, 439Jabber-RPC, 441jabber.py library, 440-441JabberBeans, 440miscellaneous resources, 441XML Cooktop, 441

open source licensing, 19, 33

OpenSSL. See SSL (Secure SocketsLayer)

org.mortbay.jetty.jar file, 303

<os> tag, 338

over-the-wire packaging (XML-RPC), 282

PpacketRecv() method, 412

packets. See messages; tags

<param> tag, 282

<params> tag, 282

Password property (JabberClientclass), 400

<pattern> tag, 306

peer groups, 369

peer-a Jabber RPC invoker, 287-289

peer-b Jabber RPC service provider,289-292

peer-c Jabber RPC service provider,293-294

PeerGroup class, 376

PeerGroupFactory class, 376

peers (JXTA)edge peers, 370endpoints, 368enumerating, 391-392peer advertisements, 366-368peer groups, 369, 376proxy peers, 370relay peers, 370rendezvous peers, 370

peers command, 374

penalty attribute (<karma> tag), 47

Perl, Net::Jabber, 421-424connectToJabber() method, 422-423installing, 422messageCB() method, 423pollJabber() method, 423presenceCB() method, 424sendMsg() method, 423sendPresence() method, 424

pictures, sharing between clients. SeeImageViewer application

pictureViewer.py file, 110-114

pipe service, 369

pipeMsgEvent() method, 396

pipes, 369, 376, 396

PipeService class, 376

19 0672325365 Index 1/21/05 2:27 PM Page 470

Page 488: Jabber Developer’s Handbook [Sams 2004]

471protocols

plain authentication, 236-238authenticate() method, 238code listing, 237-238error packets, 237<iq> packets, 236mod_auth_plain module, 238

pollJabber() method, 423

Port property (JabberClient class),400

presence information, 23AIM (AOL Instant Messenger) users,

150away, 89chat, 89client presence information, 138-141dnd, 89normal, 89<presence> tag, 84-87, 349

<error> subelement, 90from attribute, 88id attribute, 88<priority> subelement, 90show subelement, 89simple Telnet example, 76<status> subelement, 89to attribute, 88type attribute, 88-89

xa (extended away), 89<presence> tag, 84-87, 349

<error> subelement, 90from attribute, 88id attribute, 88<priority> subelement, 90show subelement, 89simple Telnet example, 76<status> subelement, 89to attribute, 88type attribute, 88-89

Presence() method, 404

presenceCB() method, 32, 121, 424

<priority> tag, 90

<privacy> tag, 65

<private> tag, 65

privkey.pem file, 262

probe type attribute (<presence>tag), 88

Process() method, 118, 214

propogate pipes, 369

protocolsDNS (Domain Name System), 51,

124JXTA, 365-366. See also Jabber-to-

JXTA bridgeadvertisements, 366-368, 379-380classes, 375-376commands, 372-375configuration, 371discovery service, 369downloading, 370installation, 370-371Java Binding API, 375-376membership service, 369messages, 368modules, 369peer groups, 369peers, 368-370pipe service, 369pipes, 369resolver service, 369services, 369URNs (uniform resource names), 366Web site, 370

SOAP (Simple Object AccessProtocol), 283-284

TCP/IP (Transmission ControlProtocol/Internet Protocol), 22

UDDI (Universal Description,Discovery, and Integration), 285

How can we make this index more useful? Email us at [email protected]

19 0672325365 Index 1/21/05 2:27 PM Page 471

Page 489: Jabber Developer’s Handbook [Sams 2004]

472 protocols

Web services protocol stack, 277-278WSDL (Web Services Description

Language), 284-285XML-RPC, 278-283

data types, 282-283Jabber and, 286Jabber-RPC object lessons, 296JabberRPCPeerA.py invoker, 287-289JabberRPCPeerB.py service provider,

289-292JabberRPCPeerC.py service provider,

292-295method call XML encoding, 281method response XML encoding, 282over-the-wire packaging, 282reallySimpleXMLRPCInvokee.py

service, 280reallySimpleXMLRPCInvoker.py

client, 278-279transport mechanics, 279XML-RPC wrapped for Jabber trans-

port, 295proxy peers, 370

public boolean register() method, 27

public key certificates, 258

<public> tag, 65

PythonActiveState ActivePython, 330ImageViewer application

callback handlers, 119ConnectToJabber() method, 119Jabber library capabilities, 119JabberHandler class, 118jabberHandler.py, 115-117jabberLogin.py, 114messageCB() method, 121pictureViewer.py, 110-114presenceCB() method, 121

Process() method, 118sendToRoster() method, 120

JabberPy, 414user creation script

callbacks, 32NewUser class, 31newUser.py code listing, 30usage generator, 32user registration, 31

Web site, 330win32all extensions, 330

Q-Rquerying for agents, 134, 136

queued messages, sending, 394

<queuetimeout> tag, 50

<random> tag, 308-309

<rate> tag, 46

ReaderThread class, 335-336

ReadEventLog() method, 337

real-time messaging, 22-23

reallySimpleXMLRPCInvokee.pyservice, 280

reallySimpleXMLRPCInvoker.pyclient, 278-279

<receiptRequested> tag, 436

receivedData() method, 362-363

receivedPacket() method, 230-231,254, 321-323, 390-392

registerNewUser() method, 31

registration, 57-58AIM (AOL Instant Messenger) users,

149-150AliceJabber listener, 324disabling automatic registration,

231-232<iq> result packets, 227

19 0672325365 Index 1/21/05 2:27 PM Page 472

Page 490: Jabber Developer’s Handbook [Sams 2004]

473resources

inventory management service, 214,221

jabber:iq:register namespace, 187Java, 27-28new user registration example

code listing, 227-229ConnectionBean class, 230output, 231receivedPacket() method, 230-231running, 230-231

Python, 31registration data, 232registration requests, 226-227reports service, 186

code listing, 193-194giveInstructions() method, 187handleRegistration() method, 188<instructions> tag, 188<iq> get/set packets, 187-188

relay peers, 370

remote messaging, 127-129

Remote Procedure Call (RPC). SeeXML-RPC

Rendezvous (Apple OS X), 370

rendezvous peers, 370

<reply> tag, 57

replyWithError() method, 255, 257

replyWithLogin() method, 255

ReportData class, 189-190

ReportData handler, 193

ReportDataBuilder class, 191-192

ReportDataHandler class, 192-193

reports service, 179-180code listing, 180-186registration, 186

code listing, 193-194giveInstructions() method, 187handleRegistration() method, 188

<instructions> tag, 188<iq> get/set packets, 187-188jabber:iq:register namespace, 187

ReportData class, 189-190ReportData handler configuration,

193ReportDataBuilder class, 191-192ReportDataHandler class, 192-193timed report generation, 196-198

formatReport() method, 197generateReports() method, 196-197handleSearchResult() method, 197ReportTask() method, 196

XDB custom storage, 193-196global.xdb file, 196handleXDB() method, 195initCache() method, 195XDBBuilder class, 194

ReportTask() method, 196

requestsbrowse requests, 169-170registration requests, 226-227search requests, 174-175version information requests, 338-339

resetmeter attribute (<karma> tag),47

resolver service, 369

Resource property (JabberClientclass), 400

<resource> tag, 56

resources. See also Web sitesAlicebot, 440Flash Messaging Libraries, 440Jabber clients, 439Jabber libraries, 440Jabber protocol, 439Jabber servers, 439Jabber-RPC, 441jabber.py library, 440-441

How can we make this index more useful? Email us at [email protected]

19 0672325365 Index 1/21/05 2:27 PM Page 473

Page 491: Jabber Developer’s Handbook [Sams 2004]

474 resources

JabberBeans, 440miscellaneous resources, 441XML Cooktop, 441

responding to version requests,339-341

response_suffix string, 347

restore attribute (<karma> tag), 47

result value (<iq> tag), 81

ResultSet object, 168

rlogger service, 51-52, 124

<roster> tag, 56

rosterCB() method, 427

rosters, returning, 136-138

roster_cmd() method, 420

ROT13 method, 280, 291-292

<route> tag, 124, 128-137, 247-249

RPC (Remote Procedure Call). SeeXML-RPC

rpm command, 260

rssAccount.py, 224

rssClient.py, 223

rssComponent.py, 216-221

RubyImageViewer application, 100

Bitmapper.rb, 101-106connectToJabber() method, 108-109disconnectFromJabber() method, 109ImageWindow class, 106-107sendJabberImage() method, 107-108

jabber4r, 424-427connectToJabber() method, 425installing, 425messageCB() method, 426rosterCB() method, 427sendMsg() method, 425sendPresence() method, 426subscriptionCB() method, 426-427

run() method, 336, 348

running Alicebot, 324-325

Ss2s (server-to-server) service, 124

default configuration, 49-50<dialback> tag, 50-51<idletimeout> tag, 50<ip> tag, 50<maxhosts> tag, 50<queuetimeout> tag, 50

Sash Web site, 441

scriptssimple Telnet script, 73-77

<iq> messages, 74-75<message> messages, 76-77<presence> messages, 76

user creation scriptsJava, 25-30Python, 30-32

search requests, 174-175

search() method, 177-178

searching databasescode listing, 177-179getColumns() method, 176getSearchInstructions() method, 175handleSearch() method, 174-175search form, 176-177search instructions, 175-176search requests, 174-175search result packets, 178-179search() method, 177-178sendError() method, 175

<secret> tag, 65

Secure Sockets Layer. See SSL

19 0672325365 Index 1/21/05 2:27 PM Page 474

Page 492: Jabber Developer’s Handbook [Sams 2004]

475sending messages

security, 22, 225client authentication

AuthBase class, 234-236authenticate() method, 234-236authentication modules, 233custom authentication, 245-257digest authentication, 238-242<iq> get requests, 234plain authentication, 236-238zero-knowledge authentication,

242-245client registration

disabling automatic registration,231-232

<iq> result packets, 227new user registration example,

227-231registration data, 232registration requests, 226-227

server-to-server connection authenti-cation, 265-268

SSL (Secure Sockets Layer)Certificate message, 258checking installation of, 260ClientChangeCipherSpec message, 259ClientFinished message, 259ClientHello message, 258ClientKeyExchange message, 259configuration, 48ConnectionBeanSSL class, 263enabling, 260-265handshake process, 258Jabber Messages message, 259public key certificates, 258ServerChangeCipherSpec message, 259ServerFinished message, 259ServerHello message, 258ServerHelloDone message, 258

self.wait_handle event, 336

sendAndWait() method, 390

sendData() method, 361

sendError() method, 175

sendEvent() method, 337

sending messagescross-language example, 414-418from C, 409-413iksemel application, 408-413Jabber-Net C# application

building, 407code listing, 400-402InitializeExcel() method, 404InitializeJabber() method, 404newIQ() method, 404newMessage() method, 404newPresence() method, 404Presence() method, 404UpdateExcel() method, 404UpdatePresence() method, 404

Jabber-Net Visual Basic applicationbuilding, 407code listing, 404-406

jabber4r, 424-427connectToJabber method, 425installing, 425messageCB method, 426rosterCB method, 427sendMsg method, 425sendPresence method, 426subscriptionCB method, 426-427

JabberBeans, 413-414Jabberlib, 418-421

auth_result method, 420connectToJabber method, 419roster_cmd method, 420sendMsg method, 420sendPresence method, 421

How can we make this index more useful? Email us at [email protected]

19 0672325365 Index 1/21/05 2:27 PM Page 475

Page 493: Jabber Developer’s Handbook [Sams 2004]

476 sending messages

Net::Jabber, 421connectToJabber method, 422-423installing, 422messageCB method, 423pollJabber method, 423presenceCB method, 424sendMsg method, 423sendPresence method, 424

JXTA messagesqueued messages, 394sendToPipe() method, 394-395to everyone on roster, 393

simple Telnet script, 73-77sendJabberImage() method, 107-108

sendMsg method, 420, 423-425

sendPresence method, 421, 424-426

sendToAll() method, 393-394

sendToPipe() method, 394-395

<sequence> tag, 243

Server property (JabberClient class),400

server-to-server connection authen-tication, 265-268

server-to-server service. See s2s service

ServerChangeCipherSpec message,259

ServerFinished message, 259

ServerHello message, 258

ServerHelloDone message, 258

servers, 123, 155-156, 273-274. Seealso services

administration, 58-59Alicebot, 299-300

AIML (Artificial Intelligence MarkupLanguage), 306-316

AIML folder, 302-303Alice-Jabber architecture, 316-318Alice Java code tree, 317-318

Alicebot Web site, 440AliceJabber class, 318-323configuration files, 303-305ConnectionBean object, 323defined, 300-301design, 301downloading, 302goals, 300lib folder, 303listener registry, 324running, 324-325Web sites, 301why it works, 301

browsable agents, 142-147client initialization

authentication, 130-134client/server connections, 130fetching roster, 136-138presence information, 138-141querying for agents, 134-136

client/server interaction, 14-17configuration

AIM (AOL Instant Messenger) gate-way, 148

command-line options, 40jabber.xml file, 40-42multiple virtual Jabber servers, 41

database service, 156browsing databases, 169-173code listing, 158-165configuring, 157-158connecting to server, 166-167JDBC (Java Database Connectivity),

167-168searching databases, 174-179

dnsrv service, 124downloading, 35-36

19 0672325365 Index 1/21/05 2:27 PM Page 476

Page 494: Jabber Developer’s Handbook [Sams 2004]

477services

installationLinux, 36-39Unix, 36-39Windows, 39

instant messaging gateways, 148-153inventory management service,

198-200import statements, 208init() method, 213InvComponent.py, 200-207inventoryClient.py, 207<iq> messages, 208-210iqCB() method, 209isAvailable() method, 213jabber.xml configuration, 211jabberd connection, 212-213messageCB() method, 211pickled client list, 211-212process() method, 214registration, 214, 221rssAccount.py, 224rssClient.py, 223rssComponent.py, 216-221setOnline() method, 213setShow() method, 213setStatus() method, 213

messagingclient-to-client messages, 125-127remote messaging, 127-129

online resources, 439reports service, 179-180

code listing, 180-186registration, 186-188, 193-194ReportData class, 189-190ReportData handler configuration, 193ReportDataBuilder class, 191-192ReportDataHandler class, 192-193

timed report generation, 196-198XDB custom storage, 193-196

s2s service, 49-51, 124server-to-server connection authenti-

cation, 265-268starting, 43-44stopping, 43troubleshooting, 43-44vCards

configuration, 57synchronization, 59

<service> tag, 40, 64, 143

services, 11advantages, 277browsing, 60c2s service, 48-49, 123client-based services, 17-19conference service, 63-66, 124database service, 156

browsing databases, 169-173code listing, 158-165configuring, 157-158connecting to server, 166-167JDBC (Java Database Connectivity),

167-168searching databases, 174-179

default configuration, 41discovery service, 369dnsrv service, 51, 124elogger, 52-53, 124finding, 13-14history of, 276

client/server architecture, 274-275servers/glass terminals, 273-274Web architectures, 275

inventory management service,198-200

import statements, 208init() method, 213

How can we make this index more useful? Email us at [email protected]

19 0672325365 Index 1/21/05 2:27 PM Page 477

Page 495: Jabber Developer’s Handbook [Sams 2004]

478 services

InvComponent.py, 200-207inventoryClient.py, 207<iq> messages, 208-210iqCB() method, 209isAvailable() method, 213jabber.xml configuration, 211jabberd connection, 212-213messageCB() method, 211pickled client list, 211-212process() method, 214registration, 214, 221rssAccount.py, 224rssClient.py, 223rssComponent.py, 216-221setOnline() method, 213setShow() method, 213setStatus() method, 213

io service, 46-48JSM (Jabber Session Manager), 41,

53-61, 123JUD (Jabber User Directory), 61-63,

124JXTA, 369library modules, 11membership service, 369pipe service, 369protocol stack, 277-278reports service, 179-180

code listing, 180-186registration, 186-188, 193-194ReportData class, 189-190ReportData handler configuration, 193ReportDataBuilder class, 191-192ReportDataHandler class, 192-193timed report generation, 196-198XDB custom storage, 193-196

resolver service, 369rlogger, 51-52, 124

s2s service, 49-51, 124SOAP (Simple Object Access

Protocol), 283-284STDIO, 13system logger service, 329-330

code listing, 330-335goUnix() method, 337goWin32() method, 336ReaderThread class, 335-336ReadEventLog() method, 337run() method, 336self.wait_handle event, 336sendEvent() method, 337

TCP/IP sockets, 11-12WinJab, 66-68xdb (XML database), 44-46, 124XML-RPC, 278-283

data types, 282-283Jabber and, 286Jabber-RPC object lessons, 296JabberRPCPeerA.py invoker, 287-289JabberRPCPeerB.py service provider,

289-292JabberRPCPeerC.py service provider,

292-295method call XML encoding, 281method response XML encoding, 282over-the-wire packaging, 282reallySimpleXMLRPCInvokee.py

service, 280reallySimpleXMLRPCInvoker.py

client, 278-279transport mechanics, 279XML-RPC wrapped for Jabber trans-

port, 295UDDI (Universal Description,

Discovery, and Integration), 285WSDL (Web Services Description

Language), 284-285

19 0672325365 Index 1/21/05 2:27 PM Page 478

Page 496: Jabber Developer’s Handbook [Sams 2004]

479<ssl> tag

session manager. See JSM (JabberSession Manager)

sessions service. See JSM (JabberSession Manager)

set value (<iq> tag), 80

set() method, 28

setOnline() method, 213

setShow() method, 213

setStatus() method, 213

<settype> tag, 57

setup() method, 254

SGML (Standard GeneralizedMarkup Language), 435

SHA-1 algorithm, 239

sharing pictures between clients,99-100

Python applicationcallback handlers, 119ConnectToJabber() method, 119Jabber library capabilities, 119JabberHandler class, 118jabberHandler.py, 115-117jabberLogin.py, 114messageCB() method, 121pictureViewer.py, 110-114presenceCB() method, 121Process() method, 118sendToRoster() method, 120

Ruby application, 100Bitmapper.rb, 101-106connectToJabber() method, 108-109disconnectFromJabber() method, 109ImageWindow class, 106-107sendJabberImage() method, 107-108

shell scripts. See scripts

<show> tag, 56, 86, 89

SOAP (Simple Object AccessProtocol), 283-284

sockets. See SSL (Secure SocketsLayer)

software, Cygwin Unix tools, 35

spreadsheets, maintaining fromJabber

C# applicationbuilding, 407code listing, 400-402InitializeExcel() method, 404InitializeJabber() method, 404newIQ() method, 404newMessage() method, 404newPresence() method, 404Presence() method, 404UpdateExcel() method, 404UpdatePresence() method, 404

Visual Basic applicationbuilding, 407code listing, 404-406

SSL (Secure Sockets Layer)Certificate message, 258checking installation of, 260ClientChangeCipherSpec message,

259ClientFinished message, 259ClientHello message, 258ClientKeyExchange message, 259ConnectionBeanSSL class, 263configuration, 48enabling, 260-265handshake process, 258Jabber Messages message, 259public key certificates, 258ServerChangeCipherSpec message,

259ServerFinished message, 259ServerHello message, 258ServerHelloDone message, 258

<ssl> tag, 48-49, 262-263

How can we make this index more useful? Email us at [email protected]

19 0672325365 Index 1/21/05 2:27 PM Page 479

Page 497: Jabber Developer’s Handbook [Sams 2004]

480 Standard Generalized Markup Language

Standard Generalized MarkupLanguage (SGML), 435

starting Jabber server, 43-44

startJabber() method, 359-360,389-390

startJxta() method, 387-388

startup.xml file (Alicebot), 303-305

stateful information, 276

<status> tag, 86, 89

STDIO, 13

stopHandler() method, 193

stopping Jabber server, 43

<stream> tag, 78, 130

streams, 438

string data type, 282

stringsresponse_suffix, 347string data type, 282

structures, my data, 411

<subject> tag, 56, 95

subscribe type attribute (<presence>tag), 88

subscribed type attribute (<presence> tag), 89

subscriptionCB() method, 426-427

Suchman, Lucy, 9

synchronizing vCards, 59

syslog file, 328

system administration. See adminis-tration

system control scriptcode listing, 344-347ExecThread class, 347-348<iq> packets, 349messageCB() method, 348<nick> tag, 349<presence> packets, 349response_suffix string, 347run() method, 348

system events, monitoring, 327system logger service, 329-337Unix syslog file, 328Windows Event Viewer, 328

system logger service, 329-330code listing, 330-335goUnix() method, 337goWin32() method, 336ReaderThread class, 335-336ReadEventLog() method, 337run() method, 336self.wait_handle event, 336sendEvent() method, 337

<system> tag, 311

Ttables, listing, 172

tags (XML)<!— —>, 436-437<alias>, 49<allow>, 47-48attributes of, 436<authtime>, 49<body>, 56, 436

jabber:x:event attachments, 93-94XML CDATA, 94

<bots>, 304<browse>, 13, 42, 144<category>, 306<continue>, 57<deny>, 47-48<dialback>, 50-51<digest>, 241<display>, 340<error>, 56, 90, 96, 257<foo>, 139<forward>, 56<from>, 56<group>, 56

19 0672325365 Index 1/21/05 2:27 PM Page 480

Page 498: Jabber Developer’s Handbook [Sams 2004]

481tags

<history>, 65<host>, 41<hosts>, 143<idletimeout>, 50, 268<info>, 436<init>, 49<instructions>, 144-145, 188<io>, 262<ip>, 50, 262<iq>, 20-21, 78, 133, 338, 349

authentication get requests, 234browse requests, 169-170client registration, 227from attribute, 79id attribute, 79inventory management service,

208-210namespaces, 82-83plain authentication, 236-237reports service registration, 187search requests, 174-175simple Telnet example, 74-75to attribute, 79type attribute, 79-82zero-knowledge authentication, 243

<item>, 147<jabberd:cmdline>, 41<javascript>, 311<jsm>, 246<karma>, 46-47<learn>, 305<listeners>, 304<load>, 45<log>, 124<logtype>, 53<maxfiles>, 45<maxhosts>, 50<maxrooms>, 65

<message>, 125, 436<body> subelement, 93-95<error> subelement, 96from attribute, 92id attribute, 92simple Telnet example, 76-77<subject> subelement, 95<thread> subelement, 91, 96to attribute, 92type attribute, 92-93<x> subelement, 96-98

<methodCall>, 282<name>, 338<nick>, 65, 349<notify>, 65<ns>, 14, 56, 142, 149<offline>, 56<os>, 338<param>, 282<params>, 282<pattern>, 306<presence>, 84-87, 349

from attribute, 88id attribute, 88simple Telnet example, 76to attribute, 88type attribute, 88-89

<priority>, 90<privacy>, 65<private>, 65<public>, 65<queuetimeout>, 50<random>, 308-309<rate>, 46<receiptRequested>, 436<reply>, 57<resource>, 56<roster>, 56

How can we make this index more useful? Email us at [email protected]

19 0672325365 Index 1/21/05 2:27 PM Page 481

Page 499: Jabber Developer’s Handbook [Sams 2004]

482 tags

<route>, 124, 128-131, 133-137,247-249

<secret>, 65<sequence>, 243<service>, 40, 64, 143<settype>, 57<show>, 56, 86, 89<ssl>, 48-49, 262-263<status>, 86, 89<stream>, 78, 130<subject>, 56, 95<system>, 311<template>, 306<that>, 311<think>, 308<thread>, 91, 96<timeout>, 45<token>, 243<topic>, 306<type>, 56<tz>, 340<unavailable>, 56<update>, 42<utc>, 340<value>, 282<vcard2jud>, 42<version>, 338<x>, 96-98, 140<xdb>, 124-126, 132, 137-140<zerok>, 132

talk command (JXTA), 375, 378-379

talk pipe advertisements, 379-380

TCL, Jabberlib, 418-421

TCP/IP (Transmission ControlProtocol/Internet Protocol), 11-12,22

Telnet script, 73-77<iq> messages, 74-75<message> messages, 76-77<presence> messages, 76

<template> tag, 306

<that> tag, 311

<think> tag, 308

<thread> tag, 91, 96

three-tier architecture, 274-275

timed report generation, 196-198formatReport() method, 197generateReports() method, 196-197handleSearchResult() method, 197ReportTask() method, 196

<timeout> tag, 45

Tkabber client, 418

to attribute<iq> tag, 79<message> tag, 92<presence> tag, 88<stream> tag, 78, 130<xdb> tag, 126

<token> tag, 243

tools, Cygwin Unix tools, 35

<topic> tag, 306

traditional applicationscharacteristics, 7-9limitations, 9-11

translating messages, 150-153

Transmission ControlProtocol/Internet Protocol(TCP/IP), 11-12, 22

troubleshooting Jabber server, 43-44

type attribute<iq> tag, 79-82<message> tag, 92-93<presence> tag, 88-89<xdb> tag, 126

<type> tag, 56

<tz> tag, 340

19 0672325365 Index 1/21/05 2:27 PM Page 482

Page 500: Jabber Developer’s Handbook [Sams 2004]

483<version> tag

U-U option (jabberd), 40

UDDI (Universal Description,Discovery, and Integration), 285

<unavailable> tag, 56

unavailable type attribute (<presence> tag), 88

Uniform Resource Identifiers(URIs), 437

uniform resource names (URNs),366

Universal Description, Discovery,and Integration (UDDI), 285

UnixCygwin Unix tools, 35Jabber server installation

configure command, 36-37make command, 37-38

syslog file, 328unsubscribe type attribute (<presence> tag), 89

unsubscribed type attribute (<presence> tag), 89

<update> tag, 42

UpdateExcel() method, 404

UpdatePresence() method, 404

updating Jabber server, 59

URIs (Uniform ResourceIdentifiers), 437

URNs (uniform resource names),366

User property (JabberClient class),400

user1.xml file, 232

user2.xml file, 232

users. See also clientsadministrative users, 58-59creating in Java

alpha.xml file, 29-30NewUser class, 27

NewUser.bat file, 26NewUser.java code listing, 25-26user registration, 27-28

creating with Pythoncallbacks, 32NewUser class, 31newUser.py code listing, 30usage generator, 32user registration, 31

JUD (Jabber User Directory), 61-63,124

registering in Java, 27-28registering with Python, 31registering with AIM (AOL Instant

Messenger), 149-150rosters, 136-138

<utc> tag, 340

V-v option (jabberd), 40

<value> tag, 282

variables, jid, 417

<vcard2jud> tag, 42

vCardsconfiguration, 57synchronizing, 59

verifying OpenSSL installation, 260

version management<iq> packets, 338<name> tag, 338<os> tag, 338requesting version information,

338-339responding to version requests,

339-341<version> tag, 338

<version> tag, 338

How can we make this index more useful? Email us at [email protected]

19 0672325365 Index 1/21/05 2:27 PM Page 483

Page 501: Jabber Developer’s Handbook [Sams 2004]

484 Visual Basic Jabber-Net sample application

Visual Basic Jabber-Net sampleapplication

building, 407code listing, 404-406

WW3C (World Wide Web Consortium)Web site, 435

Wallace, Dr. Richard, 301

Web services. See services

Web Services Description Language(WSDL), 284-285

Web sitesAlicebot, 301, 440Apache server, 350Cygwin, 35Exodus, 440Jabber, Inc., 33Jabber Software Foundation, 33Jabber Studio, 61, 123, 439Jabber-Net, 399jabberd download site, 35JabberPy, 414, 440-441JXTA, 370Python, 330Sash, 441W3C (World Wide Web Consortium),

435WinJab, 66, 439XML Cooktop, 441

welcome messages, creating, 58

wild cards, 310

win32all extensions, 330

WindowsEvent Viewer, 328Jabber server installation, 39

WinJabconfiguration, 66-68SSL (Secure Sockets Layer), 263-265Web site, 66, 439

World Wide Web Consortium (W3C)Web site, 435

WSDL (Web Services DescriptionLanguage), 284-285

X<x> tag, 96-98, 140

xa value (presence), 89

xdb (XML database), 124configuration

custom configuration, 45-46default configuration, 44-45

custom storage, 193-196global.xdb file, 196handleXDB() method, 195initCache() method, 195XDBBuilder class, 194

xdb tag, 124-126, 132, 137-140

XDBBuilder class, 194

xerces.jar file, 303

XML (Extensible MarkupLanguage), 22, 435

<!— —> tag, 436-437<alias> tag, 49<allow> tag, 47-48attributes of, 436<authtime> tag, 49<body> tag, 56, 436

jabber:x:event attachments, 93-94XML CDATA, 94

<bots> tag, 304<browse> tag, 13, 42, 144<category> tag, 306comments, 436-437<continue> tag, 57<deny> tag, 47-48<dialback> tag, 50-51<digest> tag, 241

19 0672325365 Index 1/21/05 2:27 PM Page 484

Page 502: Jabber Developer’s Handbook [Sams 2004]

485XML

<display> tag, 340<error> tag, 56, 90, 96, 257<foo> tag, 139<forward> tag, 56<from> tag, 56<group> tag, 56<history> tag, 65<host> tag, 41<hosts> tag, 143<idletimeout> tag, 50, 268<info> tag, 436<init> tag, 49<instructions> tag, 144-145, 188<io> tag, 262<ip> tag, 50, 262<iq> tag, 20-21, 78, 133, 338, 349

authentication get requests, 234browse requests, 169-170client registration, 227from attribute, 79id attribute, 79inventory management service, 208-

210namespaces, 82-83plain authentication, 236-237reports service registration, 187search requests, 174-175simple Telnet example, 74-75to attribute, 79type attribute, 79-82zero-knowledge authentication, 243

<item> tag, 147Jabber-RPC object lessons, 296<jabberd:cmdline> tag, 41<javascript> tag, 311<jsm> tag, 246<karma> tag, 46-47<learn> tag, 305

<listeners>, 304<load> tag, 45<log> tag, 124<logtype> tag, 53<maxfiles> tag, 45<maxhosts> tag, 50<maxrooms> tag, 65<message> tag, 125, 436

<body> subelement, 93-95<error> subelement, 96from attribute, 92id attribute, 92simple Telnet example, 76-77<subject> subelement, 95<thread> subelement, 91, 96to attribute, 92type attribute, 92-93<x> subelement, 96-98

<methodCall> tag, 282<name> tag, 338namespaces, 12, 20, 437<nick> tag, 65, 349<notify> tag, 65<ns> tag, 14, 56, 142, 149<offline> tag, 56<os> tag, 338<param> tag, 282<params> tag, 282<pattern> tag, 306<presence> tag, 84-87, 349

from attribute, 88id attribute, 88simple Telnet example, 76to attribute, 88type attribute, 88-89

<priority> tag, 90<privacy> tag, 65<private> tag, 65

How can we make this index more useful? Email us at [email protected]

19 0672325365 Index 1/21/05 2:27 PM Page 485

Page 503: Jabber Developer’s Handbook [Sams 2004]

486 XML

<public> tag, 65<queuetimeout> tag, 50<random> tag, 308-309<rate> tag, 46<receiptRequested> tag, 436<reply> tag, 57<resource> tag, 56<roster> tag, 56<route> tag, 124, 128-131, 133-137,

247-249<secret> tag, 65<sequence> tag, 243<service> tag, 40, 64, 143<settype> tag, 57<show> tag, 56, 86, 89simple document, 435-436<ssl> tag, 48-49, 262-263<status> tag, 86, 89<stream> tag, 78, 130streams, 438<subject> tag, 56, 95<system> tag, 311<template> tag, 306<that> tag, 311<think> tag, 308<thread> tag, 91, 96<timeout> tag, 45<token> tag, 243<topic> tag, 306<type> tag, 56<tz> tag, 340<unavailable> tag, 56<update> tag, 42<utc> tag, 340<value> tag, 282<vcard2jud> tag, 42<version> tag, 338W3C specification, 435<x> tag, 96-98, 140<xdb> tag, 124-126, 132, 137-140

xdb (XML database), 44-46, 124XML Cooktop, 441XML-RPC, 278-283

data types, 282-283Jabber and, 286JabberRPCPeerA.py invoker, 287-289JabberRPCPeerB.py service provider,

289-292JabberRPCPeerC.py service provider,

292-295method call XML encoding, 281method response XML encoding, 282over-the-wire packaging, 282reallySimpleXMLRPCInvokee.py

service, 280reallySimpleXMLRPCInvoker.py

client, 278-279transport mechanics, 279XML-RPC wrapped for Jabber trans-

port, 295<zerok> tag, 132

XML Cooktop, 441

XML-RPC, 278data types, 282-283Jabber and, 286Jabber-RPC object lessons, 296JabberRPCPeerA.py invoker, 287-289JabberRPCPeerB.py service provider,

289-292JabberRPCPeerC.py service provider,

292-295method call XML encoding, 281method response XML encoding, 282over-the-wire packaging, 282reallySimpleXMLRPCInvokee.py

service, 280reallySimpleXMLRPCInvoker.py

client, 278-279transport mechanics, 279XML-RPC wrapped for Jabber trans-

port, 295xmlns attribute (<stream> tag), 130

19 0672325365 Index 1/21/05 2:27 PM Page 486

Page 504: Jabber Developer’s Handbook [Sams 2004]

487<zerok> tag

Y-Z-Z option (jabberd), 40

zero-knowledge authentication,242-245

code listing, 244<iq> packets, 243mod_auth_Ok module, 242

<zerok> tag, 132

How can we make this index more useful? Email us at [email protected]

19 0672325365 Index 1/21/05 2:27 PM Page 487