原文はこちら。
The original article was written by Daniek Kec (Java Developer, Oracle).
https://medium.com/helidon/helidon-messaging-with-jms-bca19fd99413
Reactive messaging with JMS connector
メッセージングシステムとの統合において、JMS APIは最も人気のあるソリューションの一つです。Helidon 2.2.0では、リアクティブメッセージングをJMSに接続するための直接のサポートが提供されているのも不思議ではありません。
HelidonとApache ActiveMQをJMS経由で接続してみましょう。設定は超簡単で、コネクタ自体に集中できます。
Apache ActiveMQ
http://activemq.apache.org/
まず、dockerでActiveMQを起動します。
docker run --name='activemq' -p 61616:61616 -p 8161:8161 rmohr/activemq
JMSサーバーの準備ができたら、Helidonで遊びましょう。
JMS Messaging with Helidon MP
Helidon MPを接続するには、MicroProfile Reactive Messagingの実装、HelidonのJMSメッセージコネクタ、そしてActiveMQクライアントライブラリが必要です。
MicroProfile Reactive Messaging Specification
https://download.eclipse.org/microprofile/microprofile-reactive-messaging-1.0/microprofile-reactive-messaging-spec.html
| <dependency> | |
| <groupId>io.helidon.microprofile.messaging</groupId> | |
| <artifactId>helidon-microprofile-messaging</artifactId> | |
| </dependency> | |
| <dependency> | |
| <groupId>io.helidon.messaging.jms</groupId> | |
| <artifactId>helidon-messaging-jms</artifactId> | |
| </dependency> | |
| <dependency> | |
| <groupId>org.apache.activemq</groupId> | |
| <artifactId>activemq-client</artifactId> | |
| </dependency> |
Dependencies needed for connecting ActiveMQ over JMS
Injecting ConnectionFactory
JMS接続の準備が最も簡単なのは、名前付き、アプリケーションスコープのbeanとしてConnectionFactoryを手作業で作成することです。
| @Produces | |
| @ApplicationScoped | |
| @Named("active-mq-factory") | |
| public ConnectionFactory connectionFactory() { | |
| return new ActiveMQConnectionFactory("tcp://127.0.0.1:61616"); | |
| } |
Creating connection factory as named bean
JMSコネクタの構成は、named-factoryプロパティを使って名前付きbeanを参照できます。
| mp: | |
| messaging: | |
| connector: | |
| helidon-jms: | |
| named-factory: active-mq-factory | |
| outgoing.to-jms: | |
| connector: helidon-jms | |
| destination: messaging-test-queue-1 | |
| type: queue | |
| incoming.from-jms: | |
| connector: helidon-jms | |
| destination: messaging-test-queue-1 | |
| type: queue |
Referencing connection factory injected as bean
構成が済んだら、メッセージの送受信ができます。
Consume:
| @Incoming("from-jms") | |
| @Acknowledgment(Acknowledgment.Strategy.MANUAL) | |
| public CompletionStage<?> consumeJms(JmsMessage<String> msg) { | |
| System.out.println("JMS says: " + msg.getPayload()); | |
| return msg.ack(); | |
| } |
Publish:
| @Outgoing("to-jms") | |
| public PublisherBuilder<String> produceToJms() { | |
| return ReactiveStreams.of("test1", "test2") | |
| .map(s -> JmsMessage.builder(s) | |
| .correlationId(UUID.randomUUID().toString()) | |
| .property("stringProp", "cool property") | |
| .property("byteProp", 4) | |
| .property("intProp", 5) | |
| .onAck(() -> System.out.println("Acked!")) | |
| .build()); | |
| } |
Configuration with JNDI
JNDIを使ってConnectionFactoryをルックアップするようにすれば、構成だけでJMS接続をセットアップできます。JNDIの構成に必要なのは、jndi.jms-factoryキーを持つ接続ファクトリの名前と、初期ファクトリーのプロパティをenv-propertiesに指定するだけです。java.naming.factory.initialやjava.naming.provider.urlのような感じです。
| mp.messaging: | |
| connector: | |
| helidon-jms: | |
| jndi: | |
| # Connection factory jndi | |
| jms-factory: ConnectionFactory | |
| # Initial context properties | |
| env-properties: | |
| java.naming: | |
| factory.initial: org.apache.activemq.jndi.ActiveMQInitialContextFactory | |
| provider.url: tcp://localhost:61616 | |
| incoming.from-jms: | |
| connector: helidon-jms | |
| destination: messaging-test-queue-1 | |
| type: queue | |
| outgoing.to-jms: | |
| connector: helidon-jms | |
| destination: messaging-test-queue-1 | |
| type: queue |
Configuration with JNDI
その結果、自身で作成したbeanとしての接続ファクトリを注入した場合と同じになります。このソリューションの詳細は、Helidonリポジトリのexampleプロジェクトをお試しください。
Helidon Messaging with JMS Example
https://github.com/oracle/helidon/tree/master/examples/messaging/jms-websocket-mp
JMS Messaging with Helidon SE
Helidon SE MessagingはJMSコネクタを依存関係に持つので、必要なのはコネクタ自体の依存関係と、そして今回のサンプル用に、JMS ConnectionFactoryの実装を含むActiveMQクライアントの依存関係だけです。
| <dependency> | |
| <groupId>io.helidon.messaging.jms</groupId> | |
| <artifactId>helidon-messaging-jms</artifactId> | |
| </dependency> | |
| <!-- JMS client implenting JMS api of your choice--> | |
| <dependency> | |
| <groupId>org.apache.activemq</groupId> | |
| <artifactId>activemq-client</artifactId> | |
| </dependency> |
Dependencies needed for connecting ActiveMQ with Helidon SE
Helidon SE環境においては、基本的に接続ファクトリの管理方法には2種類(自身で作成した接続ファクトリを供給するか、JNDIを利用する)あります。
Custom ConnectionFactory
自身で作成した接続ファクトリを供給するのが、最も汎用性の高いアプローチです。複数の接続ファクトリを1個のコネクタで利用できるので、チャネル構成から接続ファクトリを参照するため、カスタム識別子を提供する必要があります。
| // Manually define JMS connection factory | |
| ActiveMQConnectionFactory connectionFactory = | |
| new ActiveMQConnectionFactory("tcp://192.168.0.123:61616"); | |
| // Setup connector | |
| JmsConnector seConn = JmsConnector.builder() | |
| .connectionFactory("jms-con-1", connectionFactory) | |
| .build(); | |
| // Prepare channels | |
| Channel<String> toJms = Channel.<String>builder() | |
| .name("to-jms") | |
| .subscriberConfig(JmsConnector.configBuilder() | |
| .queue("example_queue_1") | |
| .namedFactory("jms-con-1") | |
| .build()) | |
| .build(); | |
| Channel<String> fromJms = Channel.<String>builder() | |
| .name("from-jms") | |
| .publisherConfig(JmsConnector.configBuilder() | |
| .queue("example_queue_1") | |
| .namedFactory("jms-con-1") | |
| .build()) | |
| .build(); | |
| // Prepare emitter for interaction with non-reactive code | |
| Emitter<String> emitter = Emitter.create(toAq); | |
| // Connect everything together | |
| Messaging.builder() | |
| .connector(seConn) | |
| .emitter(emitter) | |
| .listener(fromAq, s -> { | |
| System.out.println("Message received: " + s); | |
| }) | |
| .build() | |
| .start(); | |
| // Manually send some messages | |
| emitter.send("Hello"); | |
| emitter.send("world"); | |
| emitter.send("from"); | |
| emitter.send("Active"); | |
| emitter.send("MQ!"); | |
| // Don't forget to wait for messages to arrive asynchronously! |
Complete example of sending and receiving JMS messages
1つのスニペットに全てが詰まっているのです。アブラカダブラ!✨魔法じゃないですよ。
ご覧のように、この例は全て自己完結型なので、タネも仕掛けもなく、Helidonの他の部分は必要ありません。実際には、この例をmainメソッドに置くだけで、そのまま動作します。
Configuration with JNDI
JNDIを使って接続ファクトリをルックアップすることもできます。
| // Setup connector | |
| JmsConnector seConn = JmsConnector.create(); | |
| // Prepare channels | |
| Channel<String> toJms = Channel.<String>builder() | |
| .name("to-jms") | |
| .subscriberConfig(JmsConnector.configBuilder() | |
| .queue("example_queue_1") | |
| .jndiInitialFactory(ActiveMQInitialContextFactory.class) | |
| .jndiProviderUrl("tcp://192.168.0.123:61616") | |
| .build()) | |
| .build(); |
Looking up connection factory with JNDI
これは構成を直接供給して初めてより実用的に見えることでしょう。上のMPの例からJNDIルックアップを使ったyamlの設定を見てみましょう。configがyamlを理解できるようにするために、もう一つ依存関係が必要です。
<dependency>
<groupId>io.helidon.config</groupId>
<artifactId>helidon-config-yaml</artifactId>
</dependency>
MPの例で使った構成と全く同じことがわかります。
| mp.messaging: | |
| connector: | |
| helidon-jms: | |
| jndi: | |
| # Connection factory jndi | |
| jms-factory: ConnectionFactory | |
| # Initial context properties | |
| env-properties: | |
| java.naming: | |
| factory.initial: org.apache.activemq.jndi.ActiveMQInitialContextFactory | |
| provider.url: tcp://localhost:61616 | |
| incoming.from-jms: | |
| connector: helidon-jms | |
| destination: messaging-test-queue-1 | |
| type: queue | |
| outgoing.to-jms: | |
| connector: helidon-jms | |
| destination: messaging-test-queue-1 | |
| type: queue |
Same config as in the MP example above
ではSEのメッセージングで使ってみましょう。これは全く問題ありません。というのも、JMSコネクタがMPメッセージングの構成記法を両方のフレーバーで理解しているためです。
| // Setup connector | |
| JmsConnector seConn = JmsConnector.create(); | |
| // Prepare channels | |
| Channel<String> toJms = Channel.create("to-jms"); | |
| Channel<String> fromJms = Channel.create("from-jms"); | |
| // Prepare emitter for interaction with non-reactive code | |
| Emitter<String> emitter = Emitter.create(toJms); | |
| // Connect everything together | |
| Messaging.builder() | |
| .config(Config.builder() | |
| .sources(() -> FileConfigSource.builder() | |
| .path(Paths.get("src/main/resources/application.yaml")) | |
| .build() | |
| ) | |
| .build() | |
| ) | |
| .connector(seConn) | |
| .emitter(emitter) | |
| .listener(fromJms, s -> { | |
| System.out.println("Message received: " + s); | |
| }) | |
| .build() | |
| .start(); | |
| // Manually send some messages | |
| emitter.send("Hello"); | |
| emitter.send("world"); | |
| emitter.send("from"); | |
| emitter.send("Active"); | |
| emitter.send("MQ!"); | |
| // Don't forget to wait for messages to arrive asynchronously! |
SE JMS connector with MP messaging config notation
Helidon SEにおけるJMSコネクタに関する詳細は、Helidonリポジトリのサンプルプロジェクトをお試しください。
Helidon Messaging with JMS Example
https://github.com/oracle/helidon/tree/master/examples/messaging/jms-websocket-se
HelidonのJMSコネクタが、リアクティブメッセージングを使った他のサービスへの統合の新たな扉を開きます。これらのサービスの中でも、Weblogic JMSとOracle AQは非常に興味深いものです。これらのサービスについては、次回の記事で詳しく紹介します。
HelidonのReactive Messagingについては以下のリソースをどうぞ。
Reactive JMS Connector (MP)
https://helidon.io/docs/v2/#/mp/reactivemessaging/05_jms
Reactive JMS Connector (SE)
https://helidon.io/docs/v2/#/se/reactivemessaging/05_jms
Helidon Messaging Examples
https://github.com/oracle/helidon/tree/master/examples/messaging