Spring Integration provides Channel Adapters for 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 chat 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 (e.g. "presence").
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
Spring integration provides support for XMPP via XMPP adapters which support sending and receiving both XMPP chat messages and presence changes from other entries in your roster. As with other adapters, the XMPP adapters come with support for a convenient namespace-based configuration. To configure the XMPP namespace, include the following elements in the headers of your XML configuration file:
xmlns:xmpp="http://www.springframework.org/schema/integration/xmpp" xsi:schemaLocation="http://www.springframework.org/schema/integration/xmpp http://www.springframework.org/schema/integration/xmpp/spring-integration-xmpp-2.0.xsd"
Before using inbound or outbound XMPP adapters to participate in the XMPP network, an actor must establish its XMPP connection. This
connection object could be shared by all XMPP adapters connected to a particular account. Typically this requires - at a minimum -
user
, password
, and host
.
To create a basic XMPP connection, you can utilize the convenience of the namespace.
<xmpp:xmpp-connection
id="myConnection"
user="user"
password="password"
host="host"
port="port"
resource="theNameOfTheResource"
subscription-mode="accept_all"/>
The Spring Integration adapters support receiving chat messages from other users in the system. To do this, the
Inbound Message Channel 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
if you set the extract-payload
attribute's value to 'true'
when configuring an adapter.
Configuration support for the XMPP Inbound Message Channel Adapter is provided via the inbound-channel-adapter
element.
<xmpp:inbound-channel-adapter id="xmppInboundAdapter" channel="xmppInbound" xmpp-connection="testConnection" extract-payload="false" auto-startup="true"/>
As you can see amongst the usual attributes this adapter also requires a reference to an XMPP Connection.
It is also important to mention that the XMPP inbound adapter is an event driven adapter
and a Lifecycle
implementation.
When started it will register a PacketListener
that will listen for incoming XMPP Chat Messages.
It forwards any received messages to the underlying adapter which will convert them to Spring Integration Messages and
send them to the specified channel
. It will unregister the PacketListener
when it is stopped.
You may also send chat messages to other users on XMPP using the Outbound Message Channel Adapter.
Configuration support for the XMPP Outbound Message Channel Adapter is provided via the outbound-channel-adapter
element.
<int-xmpp:outbound-channel-adapter id="outboundEventAdapter" channel="outboundEventChannel" xmpp-connection="testConnection"/>
The adapter expects as its input - at a minimum - a payload of type java.lang.String
, and a header value
for XmppHeaders.CHAT_TO
that specifies to which user the Message should be sent.
To create a message you might use the following Java code:
Message<String> xmppOutboundMsg = MessageBuilder.withPayload("Hello, XMPP!" ) .setHeader(XmppHeaders.CHAT_TO, "userhandle") .build();
Another mechanism of setting the header is by using the XMPP header-enricher support. Here is an example.
<int-xmpp:header-enricher input-channel="input" output-channel="output"> <int-xmpp:chat-to value="[email protected]"/> </int-xmpp:header-enricher>
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.
Spring Integration provides an Inbound Presence Message Channel Adapter which supports receiving Presence
events from other users in the system who happen to be on your Roster. To do this, the adapter "logs in" as a user
on your behalf, registers a RosterListener
and forwards received Presence update events as Messages to the channel
identified by the channel
attribute. The payload of the Message will be a org.jivesoftware.smack.packet.Presence
object (see http://www.igniterealtime.org/builds/smack/docs/3.1.0/javadoc/org/jivesoftware/smack/packet/Presence.html).
Configuration support for the XMPP Inbound Presence Message Channel Adapter is provided via
the presence-inbound-channel-adapter
element.
<int-xmpp:presence-inbound-channel-adapter channel="outChannel" xmpp-connection="testConnection" auto-startup="false"/>
As you can see amongst the usual attributes this adapter also requires a reference to an XMPP Connection.
It is also important to mention that this adapter is an event driven adapter and a Lifecycle
implementation.
It will register a RosterListener
when started and will unregister that RosterListener
when stopped.
Spring Integration also supports sending Presence events to be seen by other users in the network who happen to have you on their
Roster. When you send a Message to the Outbound Presence Message Channel Adapter it extracts the payload,
which is expected to be of type org.jivesoftware.smack.packet.Presence
(see http://www.igniterealtime.org/builds/smack/docs/3.1.0/javadoc/org/jivesoftware/smack/packet/Presence.html) and sends it to
the XMPP Connection, thus advertising your presence events to the rest of the network.
Configuration support for the XMPP Outbound Presence Message Channel Adapter is provided via
the presence-outbound-channel-adapter
element.
<int-xmpp:presence-outbound-channel-adapter id="eventOutboundPresenceChannel" xmpp-connection="testConnection"/>
It can also be a Polling Consumer (if it receives Messages from a Pollable Channel) in which case you would need to register a Poller.
<int-xmpp:presence-outbound-channel-adapter id="pollingOutboundPresenceAdapter" xmpp-connection="testConnection" channel="pollingChannel"> <int:poller fixed-rate="1000" max-messages-per-poll="1"/> </int-xmpp:presence-outbound-channel-adapter>
Like its inbound counterpart, it requires a reference to an XMPP Connection.
Since Spring Integration XMPP support is based on the Smack 3.1 API (http://www.igniterealtime.org/downloads/index.jsp), it is important to know a few details related to more complex configuration of the XMPP Connection object.
As stated earlier the xmpp-connection
namespace support is designed to simplify basic connection configuration and
only supports a few common configuration attributes. However, the org.jivesoftware.smack.ConnectionConfiguration
object defines about 20 attributes, and there is no real value of adding namespace support for all of them. So, for more complex connection
configurations, simply configure an instance of our XmppConnectionFactoryBean
as a regular bean, and inject a
org.jivesoftware.smack.ConnectionConfiguration
as a constructor argument to that FactoryBean. Every property
you need, can be specified directly on that ConnectionConfiguration instance (a bean definition with the 'p' namespace would work well).
This way SSL, or any other attributes, could be set directly. Here's an example:
<bean id="xmppConnection" class="org.springframework.integration.xmpp.XmppConnectionFactoryBean"> <constructor-arg> <bean class="org.jivesoftware.smack.ConnectionConfiguration"> <constructor-arg value="myServiceName"/> <property name="truststorePath" value="..."/> <property name="socketFactory" ref="..."/> </bean> </constructor-arg> </bean> <int:channel id="outboundEventChannel"/> <int-xmpp:outbound-channel-adapter id="outboundEventAdapter" channel="outboundEventChannel" xmpp-connection="xmppConnection"/>
Another important aspect of the Smack API is static initializers. For more complex cases (e.g., registering a SASL Mechanism), you may need
to execute certain static initializers. One of those static initializers is SASLAuthentication
, which allows
you to register supported SASL mechanisms. For that level of complexity, we would recommend Spring JavaConfig-style of the XMPP Connection
configuration. Then, you can configure the entire component through Java code and execute all other necessary Java code including
static initializers at the appropriate time.
@Configuration public class CustomConnectionConfiguration { @Bean public XMPPConnection xmppConnection() { SASLAuthentication.supportSASLMechanism("EXTERNAL", 0); // static initializer ConnectionConfiguration config = new ConnectionConfiguration("localhost", 5223); config.setTrustorePath("path_to_truststore.jks"); config.setSecurityEnabled(true); config.setSocketFactory(SSLSocketFactory.getDefault()); return new XMPPConnection(config); } }
For more information on the JavaConfig style of Application Context configuration, refer to the following section in the Spring Reference Manual: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-java