XMPP サポート

Spring Integration は、XMPP (英語) のチャネルアダプターを提供します。XMPP は "Extensible Messaging and Presence Protocol" の略です。

XMPP は、複数のエージェントが分散システムで互いに通信する方法を説明しています。XMPP は他の種類のアプリケーションに使用できます(また使用されています)が、標準的な使用例はチャットメッセージの送受信です。XMPP はアクターのネットワークを記述します。そのネットワーク内で、アクターは互いに直接対処し、ステータスの変化(「プレゼンス」など)をブロードキャストできます。

XMPP は、Google Talk (GTalk、GMail 内からも利用可能) や Facebook Chat など、世界最大のインスタントメッセージングネットワークの基盤となるメッセージングファブリックを提供します。多くの優れたオープンソース XMPP サーバーが利用可能です。一般的な実装としては、Openfire (英語) ejabberd (英語) の 2 つがあります。

Spring の統合は、XMPP アダプターを提供することで XMPP をサポートします。XMPP アダプターは、XMPP チャットメッセージとクライアントの名簿の他のエントリからのプレゼンス変更の両方を送受信します。

この依存関係をプロジェクトに含める必要があります。

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

他のアダプターと同様に、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 アダプターは、この接続オブジェクトを共有できます。通常、これには(少なくとも) userpasswordhost が必要です。次の例に示すように、基本的な 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 も登録します。ConnectionListener は、DEBUG ロギングレベルが有効になっている場合に接続イベントを記録します。

subscription-mode 属性は、他のユーザーからの受信サブスクリプションを処理するために名簿リスナーを開始します。この機能は、ターゲット XMPP サーバーで常に使用できるわけではありません。例: Google クラウドメッセージング (GCM) と Firebase クラウドメッセージング (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 実装です。起動すると、受信 XMPP チャットメッセージをリッスンする PacketListener を登録します。受信したメッセージはすべて基礎となるアダプターに転送され、アダプターは Spring Integration メッセージに変換して、指定された channel に送信します。停止すると、PacketListener の登録を解除します。

バージョン 4.3 以降、ChatMessageListeningEndpoint (およびその <int-xmpp:inbound-channel-adapter>)は、内部 StanzaListener 実装とともに、提供された XMPPConnection に登録される org.jivesoftware.smack.filter.StanzaFilter の挿入をサポートします。詳細については、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 に追加されます。メッセージに拡張子が 1 つしかない場合に追加されることに注意してください。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 構成の <int-xmpp:outbound-channel-adapter>)に追加されました。通常の String および org.jivesoftware.smack.packet.Message ペイロードと共に、setBody() の代わりに org.jivesoftware.smack.packet.ExtensionElement (org.jivesoftware.smack.packet.Message.addExtension() に取り込まれる)のペイロードでメッセージを送信できるようになりました。便宜上、ChatMessageSendingMessageHandler に extension-provider オプションを追加しました。org.jivesoftware.smack.provider.ExtensionElementProvider を挿入して、実行時にペイロードに対して ExtensionElement を作成します。この場合、ペイロードは XEP プロトコルに応じて JSON または XML 形式の文字列である必要があります。

XMPP プレゼンス

XMPP は、ブロードキャスト状態もサポートします。この機能を使用して、名簿に載っている人にあなたの状態の変化を見ることができます。これは、IM クライアントで常に発生します。不在ステータスを変更して不在メッセージを設定すると、名簿に載っているすべての人が、この新しい状態を反映してアイコンまたはユーザー名が変更され、新しい「不在」メッセージが表示される場合があります。通知を受信したり、状態の変化を他の人に通知したい場合は、Spring Integration の「プレゼンス」アダプターを使用できます。

受信プレゼンスメッセージチャネルアダプター

Spring Integration は受信プレゼンスメッセージチャネルアダプターを提供します。これは、名簿に載っているシステム内の他のユーザーからのプレゼンスイベントの受信をサポートします。これを行うために、アダプターはユーザーに代わってユーザーとして「ログイン」し、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 を登録し、停止時に 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 接続 Bean(前述)のデフォルトの命名規則に依存しており、アプリケーションコンテキストで構成されている XMPP 接続 Bean が 1 つしかない場合は、xmpp-connection 属性を省略できます。その場合、xmppConnection という名前の Bean が検出され、アダプターに挿入されます。

高度な構成

Spring Integration の XMPP サポートは、Smack 4.0 API(https://www.igniterealtime.org/projects/smack/ (英語) )に基づいており、XMPP 接続オブジェクトのより複雑な構成を可能にします。

前述のとおり、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 メカニズムの登録など)、特定の静的初期化子の実行が必要になる場合があります。これらの静的初期化子の 1 つは SASLAuthentication で、サポートされている SASL メカニズムを登録できます。そのレベルの複雑さのために、XMPP 接続構成に Spring Java 構成を使用することをお勧めします。そうすれば、Java コードを介してコンポーネント全体を構成し、適切なタイミングで静的初期化子を含む他のすべての必要な Java コードを実行できます。次の例は、Java で SASL(Simple Authentication and Security Layer)を使用して 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 (Javadoc) を使用して Spring Integration MessageHeaders との間でコピーされます。

DefaultXmppHeaderMapper の requestHeaderNames または replyHeaderNames プロパティで明示的に指定されていない限り、ユーザー定義のヘッダーは XMPP メッセージとの間でコピーされません。

ユーザー定義ヘッダーをマッピングする場合、値には単純なワイルドカードパターン( "thing *" または "* thing" など)を含めることもできます。

バージョン 4.1 以降、AbstractHeaderMapper (DefaultXmppHeaderMapper のスーパークラス)を使用すると、STANDARD_REQUEST_HEADERS に加えて requestHeaderNames プロパティの NON_STANDARD_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 などのリストは thing1thing2thing3 をマップしません。そのリストは、標準ヘッダーに加えて thing4 と qux をマップします。

マッピングしたい ! で始まるユーザー定義ヘッダーがある場合は、\ でエスケープすることができます: STANDARD_REQUEST_HEADERS,\!myBangHeader。その例では、標準のリクエストヘッダーと !myBangHeader がマッピングされます。

XMPP 拡張

拡張機能により、"Extensible Messaging and Presence Protocol" に "Extensible" が追加されました。

XMPP は、ネームスペースとして知られる概念をサポートするデータ形式である XML に基づいています。名前空間を通じて、元の仕様で定義されていないビットを XMPP に追加できます。XMPP 仕様では、コア機能のセットのみを意図的に説明しています。

  • クライアントがサーバーに接続する方法

  • 暗号化 (SSL/TLS)

  • 認証

  • サーバーが相互に通信してメッセージをリレーする方法

  • 他のいくつかの基本的な構成要素

これを実装すると、XMPP クライアントが作成され、任意の種類のデータを送信できます。ただし、基本以上のことを行う必要がある場合があります。例: メッセージに書式設定(太字、斜体など)を含める必要がある場合がありますが、これはコア XMPP 仕様では定義されていません。まあ、それを行う方法を作ることができますが、他の人が同じようにしない限り、他のソフトウェアはそれを解釈できません(彼らは理解できない名前空間を無視します)。

この問題を解決するために、XMPP 標準 Foundation(XSF)は、XMPP 拡張プロトコル (英語) (XEP)として知られる一連の追加ドキュメントを公開しています。一般に、各 XEP は特定のアクティビティ(メッセージのフォーマットからファイル転送、マルチユーザーチャットなど)を記述します。また、すべての人がそのアクティビティに使用する標準形式を提供します。

Smack API は、extensions および experimental  プロジェクト (英語) で多くの XEP 実装を提供します。Spring Integration バージョン 4.3 以降、既存の XMPP チャネルアダプターで任意の XEP を使用できます。

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 クラウドメッセージング (英語) (GCM)です。Smack ライブラリは、その目的のために org.jivesoftware.smackx.gcm.provider.GcmExtensionProvider を提供します。デフォルトでは、次の Maven の例に示すように、experimental.providers リソースを使用して、クラスパスの smack-experimental jar にそのクラスを登録します。

<!-- 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);

詳細については、この章で前述した受信メッセージチャネルアダプターおよび送信メッセージチャネルアダプターを参照してください。