XMPP 支援

Spring Integration 為 XMPP 提供通道介面卡。XMPP 代表“可擴充套件訊息和出席協議”(Extensible Messaging and Presence Protocol)。

XMPP 描述了一種分散式系統中多個代理之間相互通訊的方式。典型的用例是傳送和接收聊天訊息,儘管 XMPP 可以(並且正在)用於其他型別的應用程式。XMPP 描述了一個參與者網路。在該網路中,參與者可以相互直接定址並廣播狀態更改(例如“線上狀態”)。

XMPP 提供了支撐世界上一些最大的即時訊息網路(包括 Google Talk (GTalk,也可從 GMail 中獲取) 和 Facebook Chat)的訊息傳遞結構。許多優秀的開源 XMPP 伺服器都可用。兩個流行的實現是 Openfireejabberd

Spring Integration 透過提供 XMPP 介面卡來支援 XMPP,這些介面卡支援傳送和接收 XMPP 聊天訊息以及客戶端名冊中其他條目的線上狀態更改。

專案需要此依賴項

  • Maven

  • Gradle

<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-xmpp</artifactId>
    <version>7.0.0</version>
</dependency>
compile "org.springframework.integration:spring-integration-xmpp:7.0.0"

與其他介面卡一樣,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` 屬性。此連線 bean 使用預設名稱 (`xmppConnection`)。

如果 XMPP 連線失效,只要之前的連線狀態已登入(已認證),就會嘗試自動登入重新連線。我們還註冊了一個 `ConnectionListener`,如果啟用了 `DEBUG` 日誌級別,它將記錄連線事件。

`subscription-mode` 屬性啟動花名冊偵聽器以處理來自其他使用者的傳入訂閱。此功能並非始終適用於目標 XMPP 伺服器。例如,Google Cloud Messaging (GCM) 和 Firebase Cloud Messaging (FCM) 完全停用它。要關閉用於訂閱的花名冊偵聽器,您可以在使用 XML 配置時將其配置為空字串 (`subscription-mode=""`),或者在使用 Java 配置時使用 `XmppConnectionFactoryBean.setSubscriptionMode(null)`。這樣做也會在登入階段停用花名冊。有關更多資訊,請參閱 Roster.setRosterLoadedAtLogin(boolean)

XMPP 訊息

Spring Integration 提供傳送和接收 XMPP 訊息的支援。對於接收訊息,它提供一個入站訊息通道介面卡。對於傳送訊息,它提供一個出站訊息通道介面卡。

入站訊息通道介面卡

Spring Integration 介面卡支援接收來自系統中其他使用者的聊天訊息。為此,入站訊息通道介面卡代表您“登入”為使用者並接收發送給該使用者的訊息。然後,這些訊息被轉發到您的 Spring Integration 客戶端。`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 Integration 訊息並將其傳送到指定的 `channel`。停止時,它會登出 `PacketListener`。

從版本 4.3 開始,`ChatMessageListeningEndpoint`(及其 ``)支援注入 `org.jivesoftware.smack.filter.StanzaFilter`,以便在提供的 `XMPPConnection` 上註冊,同時註冊內部 `StanzaListener` 實現。有關更多資訊,請參閱 Javadoc

版本 4.3 為 `ChatMessageListeningEndpoint` 引入了 `payload-expression` 屬性。傳入的 `org.jivesoftware.smack.packet.Message` 代表評估上下文的根物件。當您使用 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"/>

介面卡要求其輸入(至少)是 `java.lang.String` 型別的有效負載和 `XmppHeaders.CHAT_TO` 的標頭值,該值指定訊息應傳送給哪個使用者。要建立訊息,您可以使用類似於以下內容的 Java 程式碼:

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

您還可以使用 XMPP 標頭豐富器支援設定標頭,如下例所示:

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

從版本 4.3 開始,`ChatMessageSendingMessageHandler`(XML 配置中的 ``)已新增資料包擴充套件支援。除了常規的 `String` 和 `org.jivesoftware.smack.packet.Message` 有效負載,現在您可以傳送一個有效負載為 `org.jivesoftware.smack.packet.XmlElement`(它被填充到 `org.jivesoftware.smack.packet.Message.addExtension()`)而不是 `setBody()` 的訊息。為了方便起見,我們為 `ChatMessageSendingMessageHandler` 添加了一個 `extension-provider` 選項。它允許您注入 `org.jivesoftware.smack.provider.ExtensionElementProvider`,它在執行時根據有效負載構建一個 `XmlElement`。在這種情況下,有效負載必須是 JSON 或 XML 格式的字串,具體取決於 XEP 協議。

XMPP 線上狀態

XMPP 還支援廣播狀態。您可以使用此功能讓在您的名冊中的人看到您的狀態更改。您的即時通訊客戶端一直在發生這種情況。您更改您的離開狀態並設定離開訊息,所有在您的名冊中的人都會看到您的圖示或使用者名稱更改以反映此新狀態,並且可能會看到您的新“離開”訊息。如果您想接收通知或通知其他人狀態更改,您可以使用 Spring Integration 的“線上狀態”介面卡。

入站線上狀態訊息通道介面卡

Spring Integration 提供了一個入站線上狀態訊息通道介面卡,它支援接收來自系統中在您名冊上的其他使用者的線上狀態事件。為此,介面卡代表您“登入”為使用者,註冊一個 `RosterListener`,並將收到的線上狀態更新事件作為訊息轉發到由 `channel` 屬性標識的通道。訊息的有效負載是一個 `org.jivesoftware.smack.packet.Presence` 物件(參見 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`,並在停止時登出該 `RosterListener`。

出站線上狀態訊息通道介面卡

Spring Integration 還支援傳送線上狀態事件,以便網路中在您的名冊上的其他使用者可以看到。當您向出站線上狀態訊息通道介面卡傳送訊息時,它會提取有效負載(預期為 `org.jivesoftware.smack.packet.Presence` 型別)並將其傳送到 XMPP 連線,從而向網路其餘部分廣播您的線上狀態事件。

`presence-outbound-channel-adapter` 元素為 XMPP 出站線上狀態訊息通道介面卡提供配置支援。以下示例展示瞭如何配置出站線上狀態訊息通道介面卡:

<int-xmpp:presence-outbound-channel-adapter id="eventOutboundPresenceChannel"
	xmpp-connection="testConnection"/>

它也可以是一個輪詢消費者(如果它從一個可輪詢的通道接收訊息),在這種情況下您需要註冊一個輪詢器。以下示例展示瞭如何操作:

<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 Connection bean 的預設命名約定(前面已描述),並且您的應用程式上下文中只配置了一個 XMPP Connection bean,則可以省略 `xmpp-connection` 屬性。在這種情況下,名為 `xmppConnection` 的 bean 將被定位並注入到介面卡中。

高階配置

Spring Integration 的 XMPP 支援基於 Smack 4.0 API (www.igniterealtime.org/projects/smack/),它允許對 XMPP Connection 物件進行更復雜的配置。

前面所述,`xmpp-connection` 名稱空間支援旨在簡化基本連線配置,並且僅支援少數常見的配置屬性。但是,`org.jivesoftware.smack.ConnectionConfiguration` 物件定義了大約 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 機制。對於這種複雜程度,我們建議使用 Spring Java 配置進行 XMPP 連線配置。這樣,您可以透過 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 Integration XMPP 介面卡自動對映標準 XMPP 屬性。預設情況下,這些屬性透過 `DefaultXmppHeaderMapper` 複製到 Spring Integration `MessageHeaders` 並從中複製。

除非 `DefaultXmppHeaderMapper` 的 `requestHeaderNames` 或 `replyHeaderNames` 屬性明確指定,否則任何使用者定義的標頭都不會複製到 XMPP 訊息或從中複製。

對映使用者定義的標頭時,值也可以包含簡單的萬用字元模式(例如“thing*”或“*thing”)。

從 4.1 版開始,`AbstractHeaderMapper`(`DefaultXmppHeaderMapper` 的超類)允許您為 `requestHeaderNames` 屬性配置 `NON_STANDARD_HEADERS` 令牌(除了 `STANDARD_REQUEST_HEADERS`),以對映所有使用者定義的標頭。

`org.springframework.xmpp.XmppHeaders` 類標識 `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,這是一種支援名稱空間概念的資料格式。透過名稱空間,您可以向 XMPP 新增原始規範中未定義的內容。XMPP 規範有意地只描述了一組核心功能:

  • 客戶端如何連線到伺服器

  • 加密 (SSL/TLS)

  • 認證

  • 伺服器之間如何相互通訊以中繼訊息

  • 其他一些基本構建塊

一旦你實現了這一點,你就擁有了一個 XMPP 客戶端,可以傳送任何你喜歡的資料。然而,你可能需要做更多基本的事情。例如,你可能需要在訊息中包含格式(粗體、斜體等),這在核心 XMPP 規範中沒有定義。好吧,你可以想出一種方法來做到這一點,但是,除非所有人都以相同的方式去做,否則其他軟體無法解釋它(它們會忽略它們無法理解的名稱空間)。

為了解決這個問題,XMPP 標準基金會 (XSF) 釋出了一系列額外的文件,稱為 XMPP 擴充套件協議 (XEPs)。通常,每個 XEP 都描述了一項特定活動(從訊息格式化到檔案傳輸、多使用者聊天等等)。它們還為每個人提供了一個標準格式,用於該活動。

Smack API 透過其 `extensions` 和 `experimental` 專案提供了許多 XEP 實現。從 Spring Integration 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`。預設情況下,它使用 `experimental.providers` 資源將該類註冊到 classpath 中的 `smack-experimental` jar 中,如下面的 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);

有關更多資訊,請參閱本章前面所述的入站訊息通道介面卡出站訊息通道介面卡

© . This site is unofficial and not affiliated with VMware.