30. XMPP Support

Spring Integration provides Channel Adapters for XMPP.

30.1 Introduction

Spring Integration provides adapters for sending and receiving both XMPP messages and status changes from other entries in your roster as well as XMPP.

XMPP describes a way for multiple agents to communicate with each other in a distributed system. The canonical use case is to send and receive instant messages, though XMPP can be, and is, used for far more applications. XMPP is used to describe a network of actors. Within that network, actors may address each other directly, as well as broadcast status changes.

XMPP provides the messaging fabric that underlies some of the biggest Instant Messaging networks in the world, including Google Talk (GTalk) - which is also available from within GMail - and Facebook Chat. There are many good open-source XMPP servers available. Two popular implementations are Openfire and ejabberd .

In XMPP, rosters (the roster corresponds to the notion of a "buddy list" in your typical IM client) are used to manage a list of other agents ("contacts", or "buddies", in an IM client) in the system, calledroster items. The roster item contains - at a minimum - the roster item's JID which is its unique ID on the network. An actor may subscribe to the state changes of another actor in the system. The subscription can be bidirectional, as well. The subscription settings determine whose status updates are broadcast, and to whom. These subscriptions are stored on the XMPP server, and are thus durable.

30.2 Using The Spring Integration XMPP Namespace

Using the Spring Integration XMPP namespace support is simple. Its use is like any other module in the Spring framework: import the XML schema, and use it to define elements. A prototypical XMPP-based integration might feature the following header. We won't repeat this in subsequent examples, because it is uninteresting.

<?xml version="1.0" encoding="UTF-8"?>

<beans:beans
    xmlns="http://www.springframework.org/schema/integration"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:xmpp="http://www.springframework.org/schema/integration/xmpp"
    xmlns:tool="http://www.springframework.org/schema/tool"
    xmlns:lang="http://www.springframework.org/schema/lang"
    xsi:schemaLocation="
http://www.springframework.org/schema/integration/xmpp
http://www.springframework.org/schema/integration/xmpp/spring-integration-xmpp.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
">
    ...

</beans:beans>

30.3 XMPP Connection

To participate in the network, an actor must connect to an XMPP server. Typically this requires - at a minimum - a user, apassword, ahost, and aport. To create an XMPP connection, you may use the XML namespace.

<xmpp:xmpp-connection
  id="myConnection"
  user="user"
  password="password"
  host="host"
  port="port"
  resource="theNameOfTheResource"
  subscription-mode="accept_all"
/>
      

30.4 XMPP Messages

30.4.1 Inbound Message Adapter

The Spring Integration adapters support receiving messages from other users in the system. To do this, the adapter "logs in" as a user on your behalf and receives the messages sent to that user. Those messages are then forwarded to your Spring Integration client. The payload of the inbound Spring Integration message may be of the raw type org.jivesoftware.smack.packet.Message, or of the type java.lang.String - which is the type of the raw Message's body property - depending on whether you specify extract-payload on the adapter's configuration or not. Inbound Messages are typically small and are text-oriented. Messages received using the adapter have a pretty standard layout, with known headers (all headers have keys defined on org.springframework.integration.xmpp.XmppHeaders):

Table 30.1. Header Values

Header NameWhat It Describes
XmppHeaders.TYPEThe value of the the org.jivesoftware.smack.packet.Message.Type enum that describes the inbound message. Possible values are: normal, chat, groupchat, headline, error.
XmppHeaders.CHATA reference to the org.jivesoftware.smack.Chat class which represents the threaded conversation containing the message.

This adapter requires a reference to an XMPP Connection. You may use the xmpp-connection element to define one. An example might look as follows:

<?xml version="1.0" encoding="UTF-8"?>

<beans:beans ... >

  <context:component-scan
            base-package="com.myxmppclient.inbound"/>

  <context:property-placeholder
            location="#{ systemProperties['user.home'] }/xmpp/xmppclient.properties"/>

  <channel id="xmppInbound"/>

  <xmpp:xmpp-connection
    id="testConnection"
      ...
  />                

    <xmpp:message-inbound-channel-adapter
        channel="xmppInbound"
        xmpp-connection="testConnection"/>
              
  <service-activator input-channel="xmppInbound"
        ref="xmppMessageConsumer"/>

</beans:beans>

In this example, the message is received from the XMPP adapter and passed to a service-activator component. Here's the declaration of theservice-activator.

package com.myxmppclient.inbound ;

import org.jivesoftware.smack.packet.Message;

import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.stereotype.Component;

@Component
public class XmppMessageConsumer {

   @ServiceActivator
   public void consume(Message input) throws Throwable {
      String text = input.getBody();
      System.out.println( "Received message: " + text  ) ;
   }

}

30.4.2 Outbound Message Adapter

You may also send messages to other users on XMPP using the outbound-message-channel-adapter adapter. The is configured like the xmpp-message-inbound-channel-adapter. The adapter takes an xmpp-connection reference. Here is a (necessarily) contrived example solution using the outbound adapter.

<?xml version="1.0" encoding="UTF-8"?>

<beans:beans ... >

    <context:component-scan
        base-package="com.myxmppproducer.outbound"/>

    <context:property-placeholder
        location="#{ systemProperties['user.home'] }/xmpp/xmppclient.properties"/>

    <beans:bean id="xmppProducer"
      class="com.myxmppproducer.outbound.XmppMessageProducer"
      p:recipient="${user.2.login}"/>

    <poller default="true" fixed-rate="10000"/>

    <xmpp:xmpp-connection
        id="testConnection"
        ...
    />

    <inbound-channel-adapter ref="xmppProducer"
      channel="outboundChannel"/>

    <channel id="outboundChannel"/>

    <xmpp:message-outbound-channel-adapter
          channel="outboundChannel"
          xmpp-connection="testConnection"/>

</beans:beans>

The adapter expects as its input - at a minimum - a payload of type java.lang.String, and a header value for XmppHeaders.CHAT_TO_USER that specifies to which the user the payload body should be sent to. To create a message destined for theoutbound-message-channel-adapter, you might use the following Java code:

          
  Message<String> xmppOutboundMsg = MessageBuilder.withPayload("Hello, world!" )
                                    .setHeader(XmppHeaders.CHAT_TO_USER, "userhandle")
                                    .build();

It's easy enough to use Java to update the XmppHeaders.CHAT_TO_USER header, and this has the advantage of dynamically updating the header at runtime in Java code. If, however, the target is more static in nature, you can configure it using the XMPP enricher support. Here is an example using the enricher. The enricher enriches the Spring Integration message to support the header values that the outbound XMPP adapters expect.

          
<channel id="input"/>
<channel id="output"/>

<xmpp:header-enricher input-channel="input" output-channel="output">
  <xmpp:message-to value="[email protected]"/>
</xmpp:header-enricher>

30.5 XMPP Presence

XMPP also supports broadcasting state. You can use this capability to let people who have you on their roster see your state changes. This happens all the time with your IM clients - you change your away status, and then set an away message, and everybody who has you on their roster sees your icon or username change to reflect this new state, and additionally might see your new "away" message. If you would like to receive notification, or notify others, of state changes, you can use Spring Integration's "presence" adapters.

The most important data for these adapters resides in the headers. The header keys are enumerated on the org.springframework.integration.xmpp.XmppHeaders class. The header keys specific to these "presence" adapters start with the token "PRESENCE_". Not all headers are available for both inbound and outbound.

Table 30.2. Header Values

Header NameWhat It Describes
XmppHeaders.PRESENCE_LANGUAGE The java.lang.String language in which the message was written.
XmppHeaders.PRESENCE_PRIORITY The priority (int) of the message. Arbitrary, but it can be used to help assign relevance to a message which in turn might be used in its handling.
XmppHeaders.PRESENCE_MODE An instance of the enum org.jivesoftware.smack.packet.Presence.Mode that has one of the following values: chat, available, away, xa, dnd
XmppHeaders.PRESENCE_TYPE An instance of the enum org.jivesoftware.smack.packet.Presence.Type that has one of the following values: available, unavailable, subscribe, subscribed, unsubscribe, unsubscribed, and error.
XmppHeaders.PRESENCE_STATUS A java.lang.String string representing the status of the agent. This corresponds to an agents "away" message.
XmppHeaders.PRESENCE_FROM A java.lang.String string representing the handle of the user whose state is being received.

30.5.1 Inbound Presence Adapter

The first adapter supports receiving messages whenever an agent on your roster has updated its state. Most of the important data comes in through the headers.

30.5.2 Outbound Presence Adapter

TBD