Martin @ Blog

software development and life.

Flower

XMPP in Java

I wrote this article a while ago, but never published it. Since it is mostly finished, I decided to put it online anyway. Unfortunately it is in Dutch, maybe I will translate it into English in the near future.

XMPP en Smack

Instant Messaging is nog relatief jong in de geschiedenis van internet en lijkt daardoor nog niet zoveel gebruikt voor de communicatie tussen de gebruiker en een applicatie als bijvoorbeeld e-mail. Het verzenden van e-mails door een applicatie is tamelijk gebruikelijk. Het versturen van berichten naar een IM-client een stuk minder. Andersom, een applicatie bedienen door middel van e-mail of instant messaging-berichten lijkt nog veel minder voor te komen, terwijl er de nodige scenario’s denkbaar zijn waarin dit een efficiente en snelle bedieningsmethode kan zijn.

Enkele jaren geleden werd er een ISO-standaard gedefinieerd voor een protocol dat gebruikt kan worden voor instant messaging. Dit was het protocol dat door het Jabber-systeem werd gebruikt, en bij het standaardiseren werd omgedoopt tot XMPP. Hoewel Jabber/XMPP als Instant Messaging-platform nog niet de populariteit heeft van bijvoorbeeld MSN Messenger, ICQ of AIM/Yahoo IM, wordt het tegenwoordig toch redelijk veel toegepast. Bekende gebruikers zijn Google Talk, Apple’s iChat en Twitter-achtige applicaties die het gebruikt als een van de interfaces naar de gebruiker.

Uiteraard zijn er diverse gratis XMPP-servers, waarvan het in Erlang geschreven ejabberd de meest bekende en meest gebruikte is. Ook zijn er voor veel programmeertalen kant-en-klare bibliotheken om het protocol te integreren in een applicatie. Een XMPP-library voor Java is Smack, dat met weinig inspanningen een applicatie voorziet van XMPP-ondersteuning. In dit artikel zal ik een korte introductie geven in het gebruik van Smack.

XMPP

Het XMPP-protocol maakt in principe gebruik van een server. Het is echter vrijwel nooit noodzakelijk om zelf een server te draaien, al kan dit wel voordelen bieden tijdens het ontwikkelen van een XMPP-applicatie, zoals volledige vrijheid in de configuratie en dergelijke. Over het algemeen is het mogelijk om een publieke Jabber/XMPP-server te gebruiken.

XMPP is een modulair protocol, waardoor er diverse uitbreidingen zijn die extra functionaliteit toevoegen. Standaard biedt XMPP zeer rudimentaire instant messaging-functionaliteit, waaronder het registeren van gebruikers en het verzenden van berichten van een gebruiker naar een andere gebruiker. Uitbreidingen bieden mogelijkheden zoals multiuser-chat, wat de functionaliteit van een chatroom biedt, vergelijkbaar met een irc-chat. Ook zijn er uitbreidingen die verzenden van databestanden mogelijk maken en audio- en videocommunicatie faciliteren. De communicatie tussen de clients en de servers vindt plaats door middel van XML-berichten, waardoor het debuggen van XMPP-applicaties erg eenvoudig is.

Om werking van XMPP is vrij simpel. Om het te kunnen gebruiken moeteen gebruiker op de server worden geregistreerd waarbij minimaal een gebruikersnaam en wachtwoord moet worden opgegeven. Hierna kan de gebruiker zich aanmelden bij de server en is het mogelijk om berichten te verzenden naar andere gebruikers. Om een bericht naar een andere gebruiker te verzenden is alleen de gebruikersnaam noodzakelijk, die de vorm gebruikersnaam@hostnaan/Resource heeft. Naast het verzenden kan ook een lijst met contactpersonen worden bijgehouden, in XMPP-terminologie heet dit een Roster. Voordat een gebruiker aan het roster kan worden toegevoegd moet deze toestemming geven, omdat het dan namelijk ook mogelijk is om te zien of de betreffende gebruiker online is. Smack biedt een behoorlijk volledige implementatie van de XMPP-standaard. De belangrijkste classes in deze bibliotheek zijn XMPPConnection, Roster, AccountManager en Chat.

Simpele XMPP-service

Het ontwikkelen van een eenvoudige XMPP-dienst is met relatief weinig code te realiseren met behulp van Smack. In dit voorbeeld zal een XMPP-service worden getoond die een aantal standaard antwoorden geeft op berichten, afhankelijk van de berichtinhoud.

Om te beginnen moet er een verbinding met een XMPP-server worden opgebouwd:

XMPPConnection xmppconn = new XMPPConnection("jabber.org");
try {
	xmppconn.connect();
} catch (XMPPException xe) { 
	xe.printStackTrace(); 
}

Er dient dus een nieuw XMPPConnection-object te worden aangemaakt, waarbij in bovenstaand voorbeeld een verbiding met de server jabber.org wordt gemaakt. De method-call connect() zorgt ervoor dat de daadwerkelijke verbinding wordt opgebouwd. Na het verbinden moet er worden ingelogd. Indien de gekozen gebruikersnaam niet bestaat, dient deze te worden geregisistreerd. In onderstaand voorbeeld is geen uitgebreide foutafhandeling aanwezig, om de code overzichtelijk te houden:

try {
	xmppconn.login("pietje", "pukje123");
} catch (XMPPException xe) {
	XMPPError err = xe.getXMPPError();
	if (err != null && err.getCode() == 404) {
		try {
			AccountManager am = xmppconn.getAccountManager();
			am.createAccount("pietje", "pukje123");
			xmppconn.login("pietje", "pukje123");
		} catch (XMPPException xe2) {
			xe2.printStackTrace();
		}
	} else {
		xe.printStackTrace();
	}
}

Eerst wordt getracht aan te melden met een gebruikersnaam en wachtwoord. Als deze niet bijkt te bestaan, stuurt de XMPP-server een xmpperror-packet met een foutcode 404, wat aangeeft dat het genoemde object niet bestaat op de server. In dat geval wordt getracht om het account aan te maken. Dit gebeurd met behulp een AccountManager-object, die wordt geleverd door de XMPPConnection-instantie. Na het aanmaken van het account wordt opnieuw geprobeerd om in te loggen.

Nu we zijn verbonden met de server kan er worden gestart met het luisteren naar binnenkomende berichten. Aangezien we op alle binnenkomende berichten willen reageren, moet er gebruik worden gemaakt van een PacketListener. Dit is een tamelijk generieke listener die alle type binnenkomende objecten kan verwerken, en waarbij dus wat filtering dient te worden toegepast:

class MyPacketListener implements PacketListener {
	public void processPacket(Packet packet) {
		if (packet instanceof Message) {
			Message mesg = (Message) packet;
			/* Code om message af te handelen */
		}
	}
}

Normaliter kan gebruik worden gemaakt van een MessageListener op een Chat tussen een gebruiker, die alleen pakketten van het type Message verwerkt en de rest negeert. In dit geval kan dat niet, omdat niet bekend is welke gebruiker een bericht naar deze client zal sturen en moet de PacketListener worden toegevoegd aan het XMPPConnection-object:

xmppconn.addPacketListener(new MyPacketListener(), new PacketTypeFilter(Message.class));

Het daadwerkelijke afhandelen van een binnenkomend bericht gebeurd dus nu in de class MyPacketListener. Om het een beetje eenvoudig te houden, reageren we op elk bericht. Als het bericht alleen bestaat uit ‘datum’ wordt de huidige datum en tijd terug gestuurd. Anders wordt er een algemeen bericht naar de verzendende gebruiker gestuurd:

Message mesg = (Message) packet;
Chat chat = xmppconn.getChatManager().createChat(mesg.getFrom(), new MessageListener() {
	public void processMessage(Chat chat, Message message) {
		System.out.println("Received message: " + message.getBody());
	}
})

try {
	if (mesg.getBody().equals("date")) {
		chat.sendMessage(DateFormat.getDateInstance().format(new Date()));
	} else {
		chat.sendMessage("De XMPP-client zegt 'hoi'");
	}					}
catch (XMPPException xe) { 
	xe.printStackTrace();
}

Voordat er een bericht gestuurd kan worden moet er een Chat-object worden aangemaakt. Hier moet ook een MessageListener aan worden gekoppeld, die in principe alleen de berichten ontvangt die worden verzonden door de gebruiker die onderdeel uitmaakt van deze chat.

Verdere mogelijkheden

Voor een applicatie die XMPP alleen maar gebruikt voor notificatie en het eventueel ontvangen van berichten van gebruikers, zouden de voorbeelden al voldoende moeten zijn. Smack (en XMPP) bieden uiteraard nog veel andere mogelijkheden. Het eerste wat men nog tegen zal komen is de mogelijkheid om te ‘subscriben’ op een gebruiker. De applicatie ontvangt dan notificaties wanneer de gebruikerstatus veranderd. Hiervoor dient echter toestemming te worden verleend door de gebruiker waarop men wil subscriben. Smack biedt de mogelijkheid om standaard toestemming te geven wanneer een subscribe-request wordt ontvangen, wat wel de beste keuze is als de gebruiker de mogelijkheid heeft om berichten naar de applicatie te sturen. In Smack wordt het subscriben op gebruikers volledig afgehandeld door de Roster-class, die bovendien een eenvoudige implementatie biedt voor het beheren van gebruikerslijsten.

Concluderend kan worden gesteld dat XMPP vele mogelijkheden biedt om notificaties te verzenden en ontvangen. De Smack-library biedt een gebruiksvriendelijke library om gebruik te maken van dit protocol en maakt het mogelijk om met relatief weinig code XMPP-ondersteuning toe te voegen aan een applicatie.

Comments are closed.