INT-3963: Add XMPP Extensions Support

JIRA: https://jira.spring.io/browse/INT-3963

* Update to Smack-4.1.6
* Introduce `stanza-filter` option for the `<int-xmpp:inbound-channel-adapter>`
* Introduce `payloadExpression` for the complex and specific `stanza` parsing, e.g. GCM packets
* Deprecate `extract-payload` in favor of `payload-expression`
* Add `ChatMessageListeningEndpointTests` test for GCM protocol
* Add `ChatMessageInboundChannelAdapterParser` test for new attributes
* Document changes

Polishing according PR comments

Extract `#extension` SpEL variable

Document the `#extension` SpEL variable
This commit is contained in:
Artem Bilan
2016-03-14 19:13:45 -04:00
committed by Gary Russell
parent 956cf275e1
commit f7c59b3b18
9 changed files with 443 additions and 55 deletions

View File

@@ -162,3 +162,8 @@ The `@InboundChannelAdapter` has now an alias `channel` attribute for regular `v
In addition the target `SourcePollingChannelAdapter` components can now resolve the target `outputChannel` bean
from its provided name (`outputChannelName` options) in late-binding manner.
See <<annotations>> for more information.
==== XMPP changes
The XMPP Extensions (XEP) are now supported by the XMPP channel adapters.
See <<xmpp-extensions>> for more information.

View File

@@ -61,7 +61,6 @@ We also register a `ConnectionListener` which will log connection events if the
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.
[source,xml]
@@ -69,7 +68,8 @@ Configuration support for the XMPP _Inbound Message Channel Adapter_ is provided
<int-xmpp:inbound-channel-adapter id="xmppInboundAdapter"
channel="xmppInbound"
xmpp-connection="testConnection"
extract-payload="false"
payload-expression="getExtension('google:mobile:data').json"
stanza-filter="stanzaFilter"
auto-startup="true"/>
----
@@ -80,6 +80,42 @@ When started it will register a `PacketListener` that will listen for incoming X
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.
Starting with _version 4.3_ the `ChatMessageListeningEndpoint` (and its `<int-xmpp:inbound-channel-adapter>`)
supports a `org.jivesoftware.smack.filter.StanzaFilter` injection to be registered on the provided `XMPPConnection`
together with an internal `StanzaListener` implementation.
See their https://www.igniterealtime.org/builds/smack/docs/latest/javadoc/org/jivesoftware/smack/XMPPConnection.html#addAsyncStanzaListener%28org.jivesoftware.smack.StanzaListener,%20org.jivesoftware.smack.filter.StanzaFilter%29[JavaDocs] for more information.
Also with the _version 4.3_ the `payload-expression` has been introduced for the `ChatMessageListeningEndpoint`.
The incoming `org.jivesoftware.smack.packet.Message` represents a root object of evaluation context.
This option is useful in case of <<xmpp-extensions>>.
For example, for the GCM protocol we can extract the body using expression:
[source,xml]
----
payload-expression="getExtension('google:mobile:data').json"
----
for the XHTML protocol:
[source,xml]
----
payload-expression="getExtension(T(org.jivesoftware.smackx.xhtmlim.packet.XHTMLExtension).NAMESPACE).bodies[0]"
----
To simplify the access to the Extension in the XMPP Message, the `extension` variable is added into the
`EvaluationContext`.
Note, it is done only when one and only one Extension is present in the Message.
The samples above with the `namespace` manipulations can be simplified to something like:
[source,xml]
----
payload-expression="#extension.json"
payload-expression="#extension.bodies[0]"
----
NOTE: The `extract-payload` option has been deprecated in favor of the new `payload-expression` one.
[[xmpp-message-outbound-channel-adapter]]
==== Outbound Message Channel Adapter
@@ -125,7 +161,7 @@ If you would like to receive notification, or notify others, of state changes, y
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).
The payload of the Message will be a `org.jivesoftware.smack.packet.Presence` object (see https://www.igniterealtime.org/builds/smack/docs/latest/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.
@@ -143,7 +179,7 @@ It will register a `RosterListener` when started and will unregister that `Roste
==== Outbound Presence Message Channel Adapter
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.
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` 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.
@@ -218,7 +254,8 @@ public class CustomConnectionConfiguration {
}
----
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
For more information on the JavaConfig style of Application Context configuration, refer to the following section
in the http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-java[Spring Reference Manual].
[[xmpp-message-headers]]
=== XMPP Message Headers
@@ -256,3 +293,110 @@ Negated patterns get priority, so a list such as
IMPORTANT: If you have a user defined header that begins with `!` that you *do* wish to map, you need to escape it with
`\` thus: `STANDARD_REQUEST_HEADERS,\!myBangHeader` and it *WILL* be mapped.
[[xmpp-extensions]]
=== XMPP Extensions
The XMPP protocol stands for **eXstensible Messaging and Presence Protocol**.
The "extensible" part is important.
XMPP is based around XML, a data format that supports a concept known as _namespacing_.
Through namespacing, you can add bits to XMPP that are not defined in the original specifications.
This is important because the XMPP specification deliberately describes only a set of core things like:
- How a client connects to a server
- Encryption (SSL/TLS)
- Authentication
- How servers can communicate with each other to relay messages
- and a few other basic building blocks.
Once you have implemented this, you have an XMPP client and can send any kind of data you like.
But that's not the end.
For example, perhaps you decide that you want to include formatting in a message (bold, italic, etc.) which is not
defined in the core XMPP specification.
Well, you can make up a way to do that, but unless everyone else does it the same way as you,
no other software will be able interpret it (they will just ignore namespaces they don't understand).
So the XMPP Standards Foundation (XSF) publishes a series of extra documents, known as
http://xmpp.org/extensions/xep-0001.html[XMPP Enhancement Proposals] (XEPs).
In general each XEP describes a particular activity (from message formatting, to file transfers, multi-user
chats and many more), and they provide a standard format for everyone to use for that activity.
The Smack API provides many XEP implementations with its `extensions` and `experimental`
http://www.igniterealtime.org/builds/smack/docs/latest/documentation/extensions/index.html[projects].
And starting with Spring Integration _version 4.3_ any XEP can be use with the existing XMPP channel adapters.
To be able to process XEPs or any other custom XMPP extensions, the Smack's `ProviderManager` pre-configuration
must be provided.
It can be done via direct usage from the `static` Java code:
[source,java]
----
ProviderManager.addIQProvider("element", "namespace", new MyIQProvider());
ProviderManager.addExtensionProvider("element", "namespace", new MyExtProvider());
----
or via `.providers` configuration file in the specific instance and JVM argument:
[source,xml]
----
-Dsmack.provider.file=file:///c:/my/provider/mycustom.providers
----
where `mycustom.providers` might be like this:
[source,xml]
----
<?xml version="1.0"?>
<smackProviders>
<iqProvider>
<elementName>query</elementName>
<namespace>jabber:iq:time</namespace>
<className>org.jivesoftware.smack.packet.Time</className>
</iqProvider>
<iqProvider>
<elementName>query</elementName>
<namespace>http://jabber.org/protocol/disco#items</namespace>
<className>org.jivesoftware.smackx.provider.DiscoverItemsProvider</className>
</iqProvider>
<extensionProvider>
<elementName>subscription</elementName>
<namespace>http://jabber.org/protocol/pubsub</namespace>
<className>org.jivesoftware.smackx.pubsub.provider.SubscriptionProvider</className>
</extensionProvider>
</smackProviders>
----
For example the most popular XMPP messaging extension is
https://developers.google.com/cloud-messaging/[Google Cloud Messaging] (GCM).
The Smack provides the particular `org.jivesoftware.smackx.gcm.provider.GcmExtensionProvider` for that and
registers that by default with the `smack-experimental` jar in the classpath using `experimental.providers` resource:
[source,xml]
----
<!-- GCM JSON payload -->
<extensionProvider>
<elementName>gcm</elementName>
<namespace>google:mobile:data</namespace>
<className>org.jivesoftware.smackx.gcm.provider.GcmExtensionProvider</className>
</extensionProvider>
----
Also the `GcmPacketExtension` is present for the target messaging protocol to parse incoming packets and build outgoing:
[source,java]
----
GcmPacketExtension gcmExtension = (GcmPacketExtension) xmppMessage.getExtension(GcmPacketExtension.NAMESPACE);
String message = gcmExtension.getJson());
----
[source,java]
----
GcmPacketExtension packetExtension = new GcmPacketExtension(gcmJson);
Message smackMessage = new Message();
smackMessage.addExtension(packetExtension);
----
See <<xmpp-message-inbound-channel-adapter>> and <<xmpp-message-outbound-channel-adapter>> above for more information.