449 lines
16 KiB
XML
449 lines
16 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
|
|
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
|
<chapter id="xmpp">
|
|
<title>XMPP Support</title>
|
|
<para>
|
|
Spring Integration provides Channel Adapters for <ulink url="http://www.xmpp.org">XMPP</ulink>.
|
|
</para>
|
|
<section id="xmpp-intro">
|
|
<title>Introduction</title>
|
|
<para>
|
|
Spring Integration provides adapters for sending and receiving both XMPP messages and status changes from other
|
|
entries in your roster as well as XMPP.
|
|
</para>
|
|
<para>
|
|
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.
|
|
</para>
|
|
<para>
|
|
<!--
|
|
todo do we have to include TM for 'Facebook', 'GMail', and 'Gtalk'?
|
|
-->
|
|
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
|
|
<ulink url="http://www.igniterealtime.org/projects/openfire/">
|
|
<citetitle>Openfire</citetitle>
|
|
</ulink>
|
|
and
|
|
<ulink url="http://www.ejabberd.im">
|
|
<citetitle>ejabberd</citetitle>
|
|
</ulink>
|
|
.
|
|
</para>
|
|
<para>
|
|
In XMPP,
|
|
<emphasis>rosters</emphasis>
|
|
(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, called<emphasis>roster items</emphasis>.
|
|
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.
|
|
|
|
</para>
|
|
|
|
|
|
</section>
|
|
<section id="xmpp-config">
|
|
<title>Using The Spring Integration XMPP Namespace
|
|
</title>
|
|
<para>
|
|
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.
|
|
|
|
|
|
<programlisting lang="xml"><![CDATA[
|
|
<?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>
|
|
]]></programlisting>
|
|
|
|
</para>
|
|
|
|
</section>
|
|
|
|
|
|
<section id="xmpp-connection">
|
|
<title>XMPP Connection</title>
|
|
<para>
|
|
To participate in the network, an actor must connect to an XMPP server. Typically this requires - at a minimum - a
|
|
<code>user</code>, a<code>password</code>, a<code>host</code>, and a<code>port</code>.
|
|
|
|
To create an XMPP connection, you may use the XML namespace.
|
|
|
|
<programlisting lang="xml"><![CDATA[<xmpp:xmpp-connection
|
|
id="myConnection"
|
|
user="user"
|
|
password="password"
|
|
host="host"
|
|
port="port"
|
|
resource="theNameOfTheResource"
|
|
subscription-mode="accept_all"
|
|
/>
|
|
]]></programlisting>
|
|
</para>
|
|
</section>
|
|
|
|
<section id="xmpp-messages">
|
|
<title>XMPP Messages</title>
|
|
<section id="xmpp-message-inbound-channel-adapter">
|
|
<title>Inbound Message Adapter</title>
|
|
<para>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<classname>
|
|
org.jivesoftware.smack.packet.Message</classname>, or of the type
|
|
<classname>java.lang.String</classname>
|
|
- which is the type of the raw<code>
|
|
Message</code>'s
|
|
<code>body</code>
|
|
property -
|
|
depending on whether you specify
|
|
<code>extract-payload</code>
|
|
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<classname>
|
|
org.springframework.integration.xmpp.XmppHeaders</classname>):
|
|
</para>
|
|
|
|
<table>
|
|
<title>Header Values</title>
|
|
|
|
<tgroup cols="2">
|
|
|
|
<colspec colname="c1"/>
|
|
<colspec colname="c2"/>
|
|
|
|
<thead>
|
|
<row>
|
|
<entry>Header Name</entry>
|
|
<entry>What It Describes</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>XmppHeaders.TYPE</entry>
|
|
<entry>The value of the
|
|
the
|
|
<code>
|
|
org.jivesoftware.smack.packet.Message.Type
|
|
</code>
|
|
enum that describes the inbound message. Possible values are:
|
|
<code>normal</code>,
|
|
<code>chat</code>,
|
|
<code>groupchat</code>,
|
|
<code>headline</code>,
|
|
<code>error</code>.
|
|
</entry>
|
|
|
|
</row>
|
|
<row>
|
|
<entry>XmppHeaders.CHAT</entry>
|
|
<entry>A reference to the
|
|
<code>org.jivesoftware.smack.Chat</code>
|
|
class which represents the
|
|
threaded conversation containing the message.
|
|
</entry>
|
|
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
|
|
</table>
|
|
|
|
|
|
<para>
|
|
This adapter requires a reference to an XMPP Connection. You may
|
|
use the
|
|
<link linkend="xmpp-connection">xmpp-connection</link>
|
|
element to define one.
|
|
|
|
An example might look as follows:
|
|
|
|
<programlisting lang="xml"><![CDATA[<?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"
|
|
...
|
|
/> ]]>
|
|
|
|
<emphasis><![CDATA[<xmpp:message-inbound-channel-adapter
|
|
channel="xmppInbound"
|
|
xmpp-connection="testConnection"/>
|
|
]]></emphasis><![CDATA[
|
|
<service-activator input-channel="xmppInbound"
|
|
ref="xmppMessageConsumer"/>
|
|
|
|
</beans:beans>]]></programlisting>
|
|
|
|
|
|
</para>
|
|
<para>
|
|
In this example, the message is received from the XMPP adapter and passed to a
|
|
<code>service-activator</code>
|
|
component. Here's the declaration of the<code>service-activator</code>.
|
|
<programlisting lang="java"><![CDATA[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 ) ;
|
|
}
|
|
|
|
}
|
|
]]></programlisting>
|
|
</para>
|
|
|
|
</section>
|
|
<section id="xmpp-message-outbound-channel-adapter">
|
|
<title>Outbound Message Adapter</title>
|
|
<para>
|
|
You may also send messages to other users on XMPP using the
|
|
<code>outbound-message-channel-adapter</code>
|
|
adapter. The is configured like the
|
|
|
|
<link linkend="xmpp-message-inbound-channel-adapter">xmpp-message-inbound-channel-adapter</link>. The
|
|
adapter takes an
|
|
<code>xmpp-connection</code>
|
|
reference.
|
|
|
|
|
|
Here is a (necessarily) contrived example solution using the outbound adapter.
|
|
|
|
<programlisting lang="xml"><![CDATA[<?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">
|
|
<interval-trigger fixed-rate="true"
|
|
interval="10" time-unit="SECONDS"/>
|
|
</poller>
|
|
|
|
<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>]]>
|
|
</programlisting>
|
|
</para>
|
|
<para>
|
|
|
|
The adapter expects as its input - at a minimum - a payload of type <classname>java.lang.String</classname>, and
|
|
a header value
|
|
for
|
|
<code>XmppHeaders.CHAT_TO_USER</code>
|
|
that specifies to which the user the payload body should be sent to.
|
|
To create a message destined for the<code>outbound-message-channel-adapter</code>, you might use the following
|
|
Java code:
|
|
|
|
<programlisting lang="java">
|
|
<![CDATA[
|
|
Message<String> xmppOutboundMsg = MessageBuilder.withPayload("Hello, world!" )
|
|
.setHeader(XmppHeaders.CHAT_TO_USER, "userhandle")
|
|
.build();
|
|
]]></programlisting>
|
|
|
|
</para>
|
|
<para>
|
|
It's easy enough to use Java to update the <code>XmppHeaders.CHAT_TO_USER</code> 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.
|
|
<programlisting lang="xml">
|
|
<![CDATA[
|
|
<channel id="input"/>
|
|
<channel id="output"/>
|
|
|
|
<xmpp:header-enricher input-channel="input" output-channel="output">
|
|
<xmpp:message-to value="test1@example.org"/>
|
|
</xmpp:header-enricher>
|
|
]]></programlisting>
|
|
</para>
|
|
|
|
</section>
|
|
</section>
|
|
<section id="xmpp-presence">
|
|
<title>XMPP Presence</title>
|
|
|
|
<para>
|
|
|
|
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.
|
|
|
|
</para> <para>
|
|
The most important data for these adapters resides in the headers. The header keys are enumerated on
|
|
the <code>org.springframework.integration.xmpp.XmppHeaders</code> class.
|
|
|
|
The header keys specific to these "presence" adapters start with the token "PRESENCE_".
|
|
|
|
Not all headers are available for both inbound and outbound.
|
|
|
|
|
|
|
|
|
|
</para> <table>
|
|
<title>Header Values</title>
|
|
|
|
<tgroup cols="2">
|
|
|
|
<colspec colname="c1"/>
|
|
<colspec colname="c2"/>
|
|
|
|
<thead>
|
|
<row>
|
|
<entry>Header Name</entry>
|
|
<entry>What It Describes</entry>
|
|
</row>
|
|
</thead>
|
|
|
|
|
|
<tbody>
|
|
<row>
|
|
<entry>XmppHeaders.PRESENCE_LANGUAGE</entry>
|
|
<entry> The <code>java.lang.String</code> language in which the message was written.
|
|
</entry>
|
|
</row>
|
|
<row>
|
|
<entry>XmppHeaders.PRESENCE_PRIORITY</entry>
|
|
<entry>
|
|
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.
|
|
</entry>
|
|
|
|
</row><row>
|
|
<entry>XmppHeaders.PRESENCE_MODE</entry>
|
|
<entry>
|
|
An instance of the enum <code>org.jivesoftware.smack.packet.Presence.Mode</code> that has one of the following values:
|
|
<code>chat,</code> <code>available,</code> <code>away,</code>
|
|
<code>xa,</code> <code>dnd</code>
|
|
</entry>
|
|
</row>
|
|
<row>
|
|
<entry>XmppHeaders.PRESENCE_TYPE</entry>
|
|
<entry>
|
|
An instance of the enum <code>org.jivesoftware.smack.packet.Presence.Type</code>
|
|
that has one of the following values:
|
|
<code>available,</code> <code>unavailable,</code> <code>subscribe,</code> <code>subscribed,</code>
|
|
<code>unsubscribe,</code> <code>unsubscribed,</code> and <code>error</code>.
|
|
|
|
|
|
</entry>
|
|
</row> <row>
|
|
<entry>XmppHeaders.PRESENCE_STATUS</entry>
|
|
<entry>
|
|
A <code>java.lang.String</code> string representing the status of the agent. This corresponds to an agents "away" message.
|
|
</entry>
|
|
</row>
|
|
<row>
|
|
<entry>XmppHeaders.PRESENCE_FROM</entry>
|
|
<entry>
|
|
A <code>java.lang.String</code> string representing the handle of the user whose state is being received.
|
|
</entry>
|
|
</row>
|
|
|
|
|
|
</tbody>
|
|
</tgroup>
|
|
|
|
</table>
|
|
<section id="xmpp-presence-inbound-channel-adapter">
|
|
<title>Inbound Presence Adapter</title>
|
|
<para>
|
|
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.
|
|
|
|
</para>
|
|
</section>
|
|
<section id="xmpp-presence-outbound-channel-adapter">
|
|
<title>Outbound Presence Adapter</title>
|
|
<para>TBD</para>
|
|
</section>
|
|
</section>
|
|
|
|
<!--<section id="xmpp-samples">
|
|
<title>XMPP Samples</title>
|
|
<para>
|
|
We really should have some samples...
|
|
</para>
|
|
</section>
|
|
-->
|
|
</chapter> |