XMPP 支持
XMPP 支持
Spring 集成为 XMPP 提供了通道适配器。 XMPP 代表“可扩展消息传递和在线状态协议”。
XMPP 描述了多个代理在分布式系统中相互通信的一种方式。 规范的用例是发送和接收聊天消息,尽管 XMPP 可以(并且正在)用于其他类型的应用程序。 XMPP 描述参与者网络。 在该网络中,参与者可以直接相互称呼并广播状态更改(例如“在线状态”)。
XMPP 提供的消息结构是世界上一些最大的即时消息网络的基础,包括 Google Talk(GTalk,也可从 GMail 中获得)和 Facebook Chat。 有许多优秀的开源 XMPP 服务器可用。 两种流行的实现是 Openfire 和 ejabberd。
Spring 集成通过提供 XMPP 适配器来提供对 XMPP 的支持,XMPP 适配器支持发送和接收 XMPP 聊天消息和来自客户端名册中其他条目的状态更改。
您需要将此依赖项包含在您的项目中:
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-xmpp</artifactId>
<version>6.0.9</version>
</dependency>
compile "org.springframework.integration:spring-integration-xmpp:6.0.9"
与其他适配器一样,XMPP 适配器支持方便的基于命名空间的配置。 要配置 XMPP 命名空间,请在 XML 配置文件的标头中包含以下元素:
xmlns:int-xmpp="http://www.springframework.org/schema/integration/xmpp"
xsi:schemaLocation="http://www.springframework.org/schema/integration/xmpp
https://www.springframework.org/schema/integration/xmpp/spring-integration-xmpp.xsd"
XMPP 连接
在使用入站或出站 XMPP 适配器参与 XMPP 网络之前,参与者必须建立其 XMPP 连接。
连接到特定帐户的所有 XMPP 适配器都可以共享此连接对象。
通常,这需要(至少)user
,password
和host
.
要创建基本的 XMPP 连接,您可以使用命名空间的便利性,如下例所示:
<int-xmpp:xmpp-connection
id="myConnection"
user="user"
password="password"
host="host"
port="port"
resource="theNameOfTheResource"
subscription-mode="accept_all"/>
为了更加方便,您可以依赖默认命名约定并省略id 属性。
默认名称 (xmppConnection ) 用于此连接 Bean。 |
如果 XMPP 连接过时,只要记录了以前的连接状态(已验证),就会使用自动登录进行重新连接尝试。
我们还注册了一个ConnectionListener
,如果DEBUG
启用日志记录级别。
这subscription-mode
属性启动 Roster 侦听器以处理来自其他用户的传入订阅。
此功能并不总是可用于目标 XMPP 服务器。
例如,Google Cloud Messaging (GCM) 和 Firebase Cloud Messaging (FCM) 完全禁用了它。
要关闭订阅的名册侦听器,您可以在使用 XML 配置 (subscription-mode=""
) 或XmppConnectionFactoryBean.setSubscriptionMode(null)
使用 Java 配置时。
这样做也会在登录阶段禁用名单。
看Roster.setRosterLoadedAtLogin(boolean)
了解更多信息。
XMPP 消息
Spring 集成支持发送和接收 XMPP 消息。 为了接收它们,它提供了一个入站消息通道适配器。 为了发送它们,它提供了一个出站消息通道适配器。
入站消息通道适配器
Spring 集成适配器支持接收来自系统中其他用户的聊天消息。
为此,入站消息通道适配器代表您以用户身份“登录”,并接收发送给该用户的消息。
然后,这些消息被转发到你的 Spring 集成客户端。
这inbound-channel-adapter
元素为 XMPP 入站消息通道适配器提供配置支持。
以下示例显示如何配置它:
<int-xmpp:inbound-channel-adapter id="xmppInboundAdapter"
channel="xmppInbound"
xmpp-connection="testConnection"
payload-expression="getExtension('google:mobile:data').json"
stanza-filter="stanzaFilter"
auto-startup="true"/>
除了通常的属性(对于消息通道适配器)外,此适配器还需要对 XMPP 连接的引用。
XMPP 入站适配器是事件驱动的,并且Lifecycle
实现。
启动时,它会注册一个PacketListener
侦听传入的 XMPP 聊天消息。
它将任何收到的消息转发到底层适配器,底层适配器将它们转换为 Spring 集成消息,并将它们发送到指定的channel
.
停止时,它会取消注册PacketListener
.
从版本 4.3 开始,ChatMessageListeningEndpoint
(及其<int-xmpp:inbound-channel-adapter>
) 支持注入org.jivesoftware.smack.filter.StanzaFilter
在提供的XMPPConnection
,以及内部的StanzaListener
实现。
有关更多信息,请参阅 Javadoc。
版本 4.3 引入了payload-expression
属性ChatMessageListeningEndpoint
.
传入的org.jivesoftware.smack.packet.Message
表示 evaluation 上下文的根对象。
当您使用 XMPP 扩展时,此选项非常有用。
例如,对于 GCM 协议,我们可以使用以下表达式提取正文:
payload-expression="getExtension('google:mobile:data').json"
以下示例提取 XHTML 协议的主体:
payload-expression="getExtension(T(org.jivesoftware.smackx.xhtmlim.packet.XHTMLExtension).NAMESPACE).bodies[0]"
为了简化对 XMPP 消息中扩展的访问,extension
变量被添加到EvaluationContext
.
请注意,当消息中只有一个扩展时,会添加它。
前面的示例显示了namespace
作可以简化为以下示例:
payload-expression="#extension.json"
payload-expression="#extension.bodies[0]"
出站消息通道适配器
您还可以使用出站消息通道适配器向 XMPP 上的其他用户发送聊天消息。
这outbound-channel-adapter
元素为 XMPP 出站消息通道适配器提供配置支持。
<int-xmpp:outbound-channel-adapter id="outboundEventAdapter"
channel="outboundEventChannel"
xmpp-connection="testConnection"/>
适配器期望其 input (至少) 是java.lang.String
以及XmppHeaders.CHAT_TO
指定应将消息发送给哪个用户。
要创建消息,您可以使用类似于以下内容的 Java 代码:
Message<String> xmppOutboundMsg = MessageBuilder.withPayload("Hello, XMPP!" )
.setHeader(XmppHeaders.CHAT_TO, "userhandle")
.build();
您还可以使用 XMPP header-enricher 支持来设置标头,如下例所示:
<int-xmpp:header-enricher input-channel="input" output-channel="output">
<int-xmpp:chat-to value="[email protected]"/>
</int-xmpp:header-enricher>
从版本 4.3 开始,数据包扩展支持已添加到ChatMessageSendingMessageHandler
(<int-xmpp:outbound-channel-adapter>
在 XML 配置中)。
除了常规的String
和org.jivesoftware.smack.packet.Message
payload 中,现在您可以发送 payload 为org.jivesoftware.smack.packet.ExtensionElement
(填充到org.jivesoftware.smack.packet.Message.addExtension()
) 而不是setBody()
.
为方便起见,我们添加了一个extension-provider
选项ChatMessageSendingMessageHandler
.
它允许您注射org.jivesoftware.smack.provider.ExtensionElementProvider
,它会构建一个ExtensionElement
针对运行时的有效负载。
在这种情况下,负载必须是 JSON 或 XML 格式的字符串,具体取决于 XEP 协议。
XMPP 存在
XMPP 还支持广播状态。 您可以使用此功能让名单中有您的人看到您的状态更改。 您的 IM 客户端一直都会发生这种情况。 您更改了 离开 状态 并 设置了 离开 消息,将您添加到名单上的每个人都会看到您的图标或用户名更改,以反映此新状态,并且可能会看到您的新 “离开” 消息。 如果你想接收通知或通知其他人状态更改,你可以使用 Spring Integration 的 “presence” 适配器。
入站在线状态消息通道适配器
Spring 集成提供了一个入站在线消息通道适配器,它支持从系统中名册上的其他用户接收在线状态事件。
为此,适配器代表您以用户身份“登录”,注册一个RosterListener
,并将收到的状态更新事件作为消息转发到由channel
属性。
消息的有效负载是一个org.jivesoftware.smack.packet.Presence
对象(请参阅 https://www.igniterealtime.org/builds/smack/docs/latest/javadoc/org/jivesoftware/smack/packet/Presence.html)。
这presence-inbound-channel-adapter
元素为 XMPP 入站在线状态消息通道适配器提供配置支持。
以下示例配置入站状态消息通道适配器:
<int-xmpp:presence-inbound-channel-adapter channel="outChannel"
xmpp-connection="testConnection" auto-startup="false"/>
除了通常的属性外,此适配器还需要对 XMPP 连接的引用。
此适配器是事件驱动的,并且Lifecycle
实现。
它注册了一个RosterListener
when started 并注销RosterListener
当停止时。
出站在线状态消息通道适配器
Spring 集成还支持发送在线状态事件,以便网络中恰好有您的其他用户看到。
当您将消息发送到出站在线状态消息通道适配器时,它会提取有效负载(预期为org.jivesoftware.smack.packet.Presence
) 并将其发送到 XMPP 连接,从而向网络的其余部分公布您的在线状态事件。
这presence-outbound-channel-adapter
元素为 XMPP 出站在线状态消息通道适配器提供配置支持。
以下示例说明如何配置出站在线状态消息通道适配器:
<int-xmpp:presence-outbound-channel-adapter id="eventOutboundPresenceChannel"
xmpp-connection="testConnection"/>
它也可以是轮询使用者(如果它从可轮询通道接收消息),在这种情况下,你需要注册一个 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>
与入站对应项一样,它需要对 XMPP 连接的引用。
如果您依赖于 XMPP 连接 Bean 的默认命名约定(如前所述),并且在应用程序上下文中只配置了一个 XMPP 连接 Bean,则可以省略xmpp-connection 属性。
在这种情况下,带有命名xmppConnection 找到并注入适配器。 |
高级配置
Spring 集成的 XMPP 支持基于 Smack 4.0 API (https://www.igniterealtime.org/projects/smack/),它允许对 XMPP Connection 对象进行更复杂的配置。
如前所述,xmpp-connection
命名空间支持旨在简化基本连接配置,并且仅支持几个常见的配置属性。
但是,org.jivesoftware.smack.ConnectionConfiguration
object 定义了大约 20 个属性,为所有这些属性添加命名空间支持没有真正的价值。
因此,对于更复杂的连接配置,您可以配置我们的XmppConnectionFactoryBean
作为常规 bean 并注入一个org.jivesoftware.smack.ConnectionConfiguration
作为构造函数参数FactoryBean
.
您可以直接在该属性上指定所需的每个属性ConnectionConfiguration
实例。
(具有 'p' 名称空间的 bean 定义将很好地工作。
这样,您可以直接设置 SSL(或任何其他属性)。
以下示例显示了如何执行此作:
<bean id="xmppConnection" class="o.s.i.xmpp.XmppConnectionFactoryBean">
<constructor-arg>
<bean class="org.jivesoftware.smack.ConnectionConfiguration">
<constructor-arg value="myServiceName"/>
<property name="socketFactory" ref="..."/>
</bean>
</constructor-arg>
</bean>
<int:channel id="outboundEventChannel"/>
<int-xmpp:outbound-channel-adapter id="outboundEventAdapter"
channel="outboundEventChannel"
xmpp-connection="xmppConnection"/>
Smack API 还提供静态初始化器,这可能会有所帮助。
对于更复杂的情况(例如注册 SASL 机制),您可能需要执行某些静态初始值设定项。
其中一个静态初始值设定项是SASLAuthentication
,它允许您注册支持的 SASL 机制。
对于该级别的复杂性,我们建议对 XMPP 连接配置使用 Spring Java 配置。
这样,您可以通过 Java 代码配置整个组件,并在适当的时间执行所有其他必要的 Java 代码,包括静态初始值设定项。
以下示例说明如何在 Java 中配置具有 SASL(简单身份验证和安全层)的 XMPP 连接:
@Configuration
public class CustomConnectionConfiguration {
@Bean
public XMPPConnection xmppConnection() {
SASLAuthentication.supportSASLMechanism("EXTERNAL", 0); // static initializer
ConnectionConfiguration config = new ConnectionConfiguration("localhost", 5223);
config.setKeystorePath("path_to_truststore.jks");
config.setSecurityEnabled(true);
config.setSocketFactory(SSLSocketFactory.getDefault());
return new XMPPConnection(config);
}
}
有关使用 Java 进行应用程序上下文配置的更多信息,请参见 Spring 参考手册中的以下部分。
XMPP 消息报头
Spring 集成 XMPP 适配器会自动映射标准 XMPP 属性。
默认情况下,这些属性将复制到 Spring 集成中或从中复制MessageHeaders
通过使用DefaultXmppHeaderMapper
.
任何用户定义的标头都不会复制到 XMPP 消息或从 XMPP 消息中复制,除非由requestHeaderNames
或replyHeaderNames
的属性DefaultXmppHeaderMapper
.
映射用户定义的标头时,值还可以包含简单的通配符模式(例如 “thing*” 或 “*thing”)。 |
从版本 4.1 开始,AbstractHeaderMapper
(DefaultXmppHeaderMapper
) 允许您配置NON_STANDARD_HEADERS
token 的requestHeaderNames
属性(除了STANDARD_REQUEST_HEADERS
) 映射所有用户定义的标头。
这org.springframework.xmpp.XmppHeaders
class 标识DefaultXmppHeaderMapper
:
-
xmpp_from
-
xmpp_subject
-
xmpp_thread
-
xmpp_to
-
xmpp_type
从版本 4.3 开始,您可以通过在模式前面加上!
.
否定模式获得优先级,因此STANDARD_REQUEST_HEADERS,thing1,thing*,!thing2,!thing3,qux,!thing1
不映射thing1
,thing2
或thing3
.
该列表确实映射了标准标头加上thing4
和qux
.
如果您有一个以! ,可以通过以下方式转义它:\ STANDARD_REQUEST_HEADERS,\!myBangHeader .
在该示例中,标准请求标头和!myBangHeader 映射。 |
XMPP 扩展
扩展将“可扩展”放在“可扩展消息传递和状态协议”中。
XMPP 基于 XML,XML 是一种支持称为命名空间的概念的数据格式。 通过命名空间,您可以向 XMPP 添加原始规范中未定义的位。 XMPP 规范特意只描述了一组核心功能:
-
客户端如何连接到服务器
-
加密 (SSL/TLS)
-
认证
-
服务器如何相互通信以中继消息
-
其他一些基本构建块
实施此作后,您就拥有了一个 XMPP 客户端,可以发送您喜欢的任何类型的数据。 但是,您可能需要做的不仅仅是基础知识。 例如,您可能需要在消息中包含格式(粗体、斜体等),这在核心 XMPP 规范中未定义。 嗯,你可以想办法做到这一点,但是,除非其他人都像你一样做,否则没有其他软件可以解释它(它们会忽略他们无法理解的命名空间)。
为了解决这个问题,XMPP 标准基金会 (XSF) 发布了一系列额外的文档,称为 XMPP 扩展协议 (XEP)。 通常,每个 XEP 都描述一个特定的活动(从消息格式到文件传输、多用户聊天等等)。 它们还为每个人提供了用于该活动的标准格式。
Smack API 提供了许多 XEP 实现,其中extensions
和experimental
项目。
从 Spring 集成版本 4.3 开始,你可以将任何 XEP 与现有的 XMPP 通道适配器一起使用。
为了能够处理 XEP 或任何其他自定义 XMPP 扩展,您必须提供 Smack 的ProviderManager
预配置。
您可以使用static
Java 代码,如下例所示:
ProviderManager.addIQProvider("element", "namespace", new MyIQProvider());
ProviderManager.addExtensionProvider("element", "namespace", new MyExtProvider());
您还可以使用.providers
配置文件,并使用 JVM 参数访问它,如下例所示:
-Dsmack.provider.file=file:///c:/my/provider/mycustom.providers
这mycustom.providers
文件可能如下所示:
<?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>https://jabber.org/protocol/disco#items</namespace>
<className>org.jivesoftware.smackx.provider.DiscoverItemsProvider</className>
</iqProvider>
<extensionProvider>
<elementName>subscription</elementName>
<namespace>https://jabber.org/protocol/pubsub</namespace>
<className>org.jivesoftware.smackx.pubsub.provider.SubscriptionProvider</className>
</extensionProvider>
</smackProviders>
例如,最流行的 XMPP 消息传递扩展是 Google Cloud Messaging (GCM)。
Smack 库提供org.jivesoftware.smackx.gcm.provider.GcmExtensionProvider
为此目的。
默认情况下,它会向smack-experimental
jar 中,使用experimental.providers
资源,如下面的 Maven 示例所示:
<!-- GCM JSON payload -->
<extensionProvider>
<elementName>gcm</elementName>
<namespace>google:mobile:data</namespace>
<className>org.jivesoftware.smackx.gcm.provider.GcmExtensionProvider</className>
</extensionProvider>
此外,GcmPacketExtension
允许目标消息传送协议解析传入数据包并构建传出数据包,如下例所示:
GcmPacketExtension gcmExtension = (GcmPacketExtension) xmppMessage.getExtension(GcmPacketExtension.NAMESPACE);
String message = gcmExtension.getJson());
GcmPacketExtension packetExtension = new GcmPacketExtension(gcmJson);
Message smackMessage = new Message();
smackMessage.addExtension(packetExtension);
有关更多信息,请参阅本章前面的 Inbound Message Channel Adapter 和 Outbound Message Channel Adapter。