JMSでAzure Service Busにメッセージを送信する

Microsoft Learnの以下コンテンツを試したのですが、一部変更が必要な所や足りない所があったので補足記事を書きます。

https://docs.microsoft.com/ja-jp/learn/modules/cloud-scale-messaging-with-jms-service-bus/

#変更：Azure Spring Boot Starter For Azure Service Bus JMSの依存性

pom.xmlに追加する依存性は現在com.microsoft.azureではなくcom.azure.springのものになっています。

```xml
<dependency>
    <groupId>com.microsoft.azure</groupId>
    <artifactId>azure-servicebus-jms-spring-boot-starter</artifactId>
    <version>2.3.3</version>
</dependency>
```

![スクリーンショット 2021-05-02 18.33.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47737/9110f0c1-5b34-9e80-caa0-e8e09c3875e0.png)

ということで以下を追加しました。

```xml
<dependency>
    <groupId>com.azure.spring</groupId>
    <artifactId>azure-spring-boot-starter-servicebus-jms</artifactId>		 
    <version>3.4.0</version>
</dependency>
```

#補足：構成パラメータ

「構成パラメーターを追加する」の部分でapplication.propertiesに以下を追加すると書いてあるのですが、知識の足りない私には```<xxxxx>```に何が入るのかわかりませんでした。

```properties
spring.jms.servicebus.connection-string=<xxxxx>
spring.jms.servicebus.idle-timeout=20000
```

ここには接続文字列を入れるようです。作成したService Busの「共有アクセスポリシー」で表示されるプライマリ接続文字列をコピペしました。

![スクリーンショット 2021-05-02 18.38.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47737/c813e697-d198-df7b-83dc-bd52db8bdf52.png)

```properties
spring.jms.servicebus.connection-string=Endpoint=sb://xxxxxxxxxx.servicebus.windows.net/;SharedAccessKeyName=xxxxxxxx;SharedAccessKey=xxxxxxxxx+xxxxx+xxxxxxxx=
spring.jms.servicebus.idle-timeout=20000
```

#追加：構成パラメータ

application.propertiesにはspring.jms.servicebus.topic-client-idとspring.jms.servicebus.pricing-tierの定義も必要でした。

なかった場合、次のエラーになります。

```
2021-05-02 10:03:08.497  WARN 53623 --- [           main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'sendController': Unsatisfied dependency expressed through field 'jmsTemplate'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'jmsTemplate' defined in class path resource [org/springframework/boot/autoconfigure/jms/JmsAutoConfiguration$JmsTemplateConfiguration.class]: Unsatisfied dependency expressed through method 'jmsTemplate' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'jmsConnectionFactory' defined in class path resource [com/azure/spring/autoconfigure/jms/NonPremiumServiceBusJMSAutoConfiguration.class]: Unsatisfied dependency expressed through method 'jmsConnectionFactory' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'spring.jms.servicebus-com.azure.spring.autoconfigure.jms.AzureServiceBusJMSProperties': Invocation of init method failed; nested exception is java.lang.NullPointerException
```

最終的には以下の定義となりました。


```properties
spring.jms.servicebus.connection-string=Endpoint=sb://xxxxxxxxxx.servicebus.windows.net/;SharedAccessKeyName=xxxxxxxx;SharedAccessKey=xxxxxxxxx+xxxxx+xxxxxxxx=
spring.jms.servicebus.topic-client-id=kikutaroservicebus
spring.jms.servicebus.idle-timeout=20000
spring.jms.servicebus.pricing-tier=basic
```

なお、spring.jms.servicebus.topic-client-idはService Busの名前です。

![スクリーンショット 2021-05-02 19.04.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47737/69e47c07-eb72-6a43-a3ad-99a32e7dc341.png)


#補足：キューの作成

ソースコードはLearnのものをそのまま使いました。

```java
package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class SendController {
    
    private static final String queue = "test-queue-jms";

    @Autowired
    private JmsTemplate jmsTemplate;

    @GetMapping("/messages")
    public String postMessage(@RequestParam String message) {
        jmsTemplate.send(queue, s -> s.createTextMessage(message));
        return message;
    }
}
```

Learnにはキューを作成する手順がない気がします。。実行すると以下のエラーになりました。

```
2021-05-02 09:57:00.674 ERROR 52938 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.jms.InvalidDestinationException: The messaging entity 'sb://kikutaroservicebus.servicebus.windows.net/test-queue-jms' could not be found. To know more visit https://aka.ms/sbResourceMgrExceptions.  TrackingId:663a3a569bea493fb4f805e6142f5c81_G25, SystemTracker:gateway7, Timestamp:2021-05-02T09:56:59 [condition = amqp:not-found]; nested exception is javax.jms.InvalidDestinationException: The messaging entity 'sb://kikutaroservicebus.servicebus.windows.net/test-queue-jms' could not be found. To know more visit https://aka.ms/sbResourceMgrExceptions.  TrackingId:663a3a569bea493fb4f805e6142f5c81_G25, SystemTracker:gateway7, Timestamp:2021-05-02T09:56:59 [condition = amqp:not-found]] with root cause

org.apache.qpid.jms.provider.exceptions.ProviderInvalidDestinationException: The messaging entity 'sb://kikutaroservicebus.servicebus.windows.net/test-queue-jms' could not be found. To know more visit https://aka.ms/sbResourceMgrExceptions.  TrackingId:663a3a569bea493fb4f805e6142f5c81_G25, SystemTracker:gateway7, Timestamp:2021-05-02T09:56:59 [condition = amqp:not-found]
        at org.apache.qpid.jms.provider.amqp.AmqpSupport.convertToNonFatalException(AmqpSupport.java:177) ~[qpid-jms-client-0.53.0.jar!/:na]
        at org.apache.qpid.jms.provider.amqp.builders.AmqpResourceBuilder.getOpenAbortExceptionFromRemote(AmqpResourceBuilder.java:299) ~[qpid-jms-client-0.53.0.jar!/:na]
        at org.apache.qpid.jms.provider.amqp.builders.AmqpResourceBuilder.handleClosed(AmqpResourceBuilder.java:185) ~[qpid-jms-client-0.53.0.jar!/:na]
        at org.apache.qpid.jms.provider.amqp.builders.AmqpResourceBuilder.processRemoteClose(AmqpResourceBuilder.java:129) ~[qpid-jms-client-0.53.0.jar!/:na]
        at org.apache.qpid.jms.provider.amqp.AmqpProvider.processUpdates(AmqpProvider.java:985) ~[qpid-jms-client-0.53.0.jar!/:na]
        at org.apache.qpid.jms.provider.amqp.AmqpProvider.onData(AmqpProvider.java:871) ~[qpid-jms-client-0.53.0.jar!/:na]
        at org.apache.qpid.jms.transports.netty.NettyTcpTransport$NettyTcpTransportHandler.channelRead0(NettyTcpTransport.java:563) ~[qpid-jms-client-0.53.0.jar!/:na]
        at org.apache.qpid.jms.transports.netty.NettyTcpTransport$NettyTcpTransportHandler.channelRead0(NettyTcpTransport.java:556) ~[qpid-jms-client-0.53.0.jar!/:na]
        at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99) ~[netty-transport-4.1.63.Final.jar!/:4.1.63.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.63.Final.jar!/:4.1.63.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.63.Final.jar!/:4.1.63.Final]
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.63.Final.jar!/:4.1.63.Final]
        at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1504) ~[netty-handler-4.1.63.Final.jar!/:4.1.63.Final]
        at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1253) ~[netty-handler-4.1.63.Final.jar!/:4.1.63.Final]
        at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1300) ~[netty-handler-4.1.63.Final.jar!/:4.1.63.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:508) ~[netty-codec-4.1.63.Final.jar!/:4.1.63.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:447) ~[netty-codec-4.1.63.Final.jar!/:4.1.63.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276) ~[netty-codec-4.1.63.Final.jar!/:4.1.63.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.63.Final.jar!/:4.1.63.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.63.Final.jar!/:4.1.63.Final]
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.63.Final.jar!/:4.1.63.Final]
        at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) ~[netty-transport-4.1.63.Final.jar!/:4.1.63.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.63.Final.jar!/:4.1.63.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.63.Final.jar!/:4.1.63.Final]
        at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) ~[netty-transport-4.1.63.Final.jar!/:4.1.63.Final]
        at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:795) ~[netty-transport-native-epoll-4.1.63.Final-linux-x86_64.jar!/:4.1.63.Final]
        at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:480) ~[netty-transport-native-epoll-4.1.63.Final-linux-x86_64.jar!/:4.1.63.Final]
        at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:378) ~[netty-transport-native-epoll-4.1.63.Final-linux-x86_64.jar!/:4.1.63.Final]
        at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989) ~[netty-common-4.1.63.Final.jar!/:4.1.63.Final]
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.63.Final.jar!/:4.1.63.Final]
        at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]
```

そこでコードにあるとおり「test-queue-jms」という名前のキューを作成します。

![スクリーンショット 2021-05-02 18.58.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47737/e483a260-3cba-bf09-842f-7e03fe9ef4b3.png)

再度実行すると動きました！！

![スクリーンショット 2021-05-02 19.18.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47737/68ebdd88-99a8-7bce-9c36-456856eb22ca.png)

キューができているか確認します。

![スクリーンショット 2021-05-02 19.19.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47737/888ac0fe-4115-0717-726a-5c33cfc514d3.png)

メッセージが届いています。

![スクリーンショット 2021-05-02 19.19.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47737/91544d03-bce0-bb6d-4f9d-2d161837466e.png)

プログラムで受信する以外に、Service Bus Explorerを使って画面上でメッセージを確認できます。便利。

![スクリーンショット 2021-05-02 19.20.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47737/c3cf9dc7-fa0b-e949-1fec-f67262500be3.png)

確認できました！

![スクリーンショット 2021-05-02 19.20.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47737/8fa70ba0-c4f4-0562-f866-93a6b5f5bb5c.png)