spring-bootspring-jmsazure-servicebus-topicsspring-kotlinazure-spring-boot

Unable to write to Azure Service Bus Topic from Spring Boot Application using azure-servicebus-jms-spring-boot-starter


I am trying to push a message onto an azure service bus topic using JMS in Spring Boot.

As you can see below I have pulled in the dependancy for azure-servicebus-jms-spring-boot-starter

I have configured @EnableJms in my Application and I also have set the connection-string in my application.yml file which I have verified to be correct and I can see it connects to the service bus correctly as can be seen in these logs -

2020-12-15 13:36:18.431  INFO 4487 --- [windows.net:-1]] o.a.qpid.jms.sasl.SaslMechanismFinder    : Best match for SASL auth was: SASL-PLAIN
2020-12-15 13:36:18.704  INFO 4487 --- [windows.net:-1]] org.apache.qpid.jms.JmsConnection        : Connection ID:MY_CONNECTION connected to remote Broker: amqps://**-****-*****.servicebus.windows.net

Once this connection is set I then try to send a message to a topic on my Service Bus using the service I have specified below however I am seeing no transaction on the Service Bus. I have verified that the destination is correct.

Because I am not consuming a subscription on this application I have not specified spring.jms.servicebus.topic-client-id=<ServiceBusSubscriptionID> as is specified to do so here

On the consuming side where I have specified the topic client ID (A different application) I have created a dummy controller to send a sample message to the Service Bus and it works fine which would lead me to believe I am missing some configuration on this application.

However I shouldn't need to specify any specific topic client ID here as I only wish to push to the Topic. Once I have the service bus connection string I should be able to push messages to any topic I specify - jmsTemplate.convertAndSend(<TOPIC_NAME>, <MESSAGE>)

@Service
class MessageServiceImpl : MessageService {

    private val logger: Logger = LoggerFactory.getLogger(this.javaClass)

    @Autowired
    lateinit var jmsTemplate: JmsTemplate

    override fun sendMessage(topic: Topic, message: Message): Result<Unit> = Result {
        logger.info("Sending message $message to topic ${topic.destination}")
        jmsTemplate.convertAndSend(topic.destination, message)
    }
}

enum class Topic(val destination: String) {
    AWARDS("awards")
}

open class Message(
    val eventTrigger: String,
    val eventTriggeredBy: String,
    val eventTimestamp: LocalDateTime,
    val eventSourceSystem: String
)
  jms:
    servicebus:
      connection-string: ${JMS_CONNECTION_STRING:''}
      idle-timeout: 180000
compile 'com.microsoft.azure:azure-servicebus-jms-spring-boot-starter:2.2.5'
@SpringBootApplication
@EnableJms
class WebServiceApplication {
    companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            SpringApplication.run(WebServiceApplication::class.java, *args)
        }
    }
}

Solution

  • The reason I was unable to send the message specified to the give topic destination was due to the fact my Message class wasn't serializable. The jms request was failing silently as a result.

    There are two possible solutions to the problem I had above.

    The first is to serialize the object as I have already mentioned -

    open class Message(
        val eventTrigger: String,
        val eventTriggeredBy: String,
        val eventTimestamp: LocalDateTime,
        val eventSourceSystem: String
    ) : Serializable
    

    However there is one main problem with this approach.

    open class Message(
        val eventTrigger: String,
        val eventTriggeredBy: String,
        val eventTimestamp: LocalDateTime,
        val eventSourceSystem: String,
        val newField: NewField
    ) : Serializable
    
    data class NewField(val sampleField: String)
    

    For example, If you add a new field that is not serializable, then this Message class will not be serializable and the requests will stop working again (This can be nasty as you may not be aware you broke it until later!)

    There is a second possible solution however, which is to marshal the class to json and send the message as a string. This solution is more complete than serializing it

    val jsonMessage = ObjectMapper().writeValueAsString(message)
    jmsTemplate.convertAndSend(topic.destination, jsonMessage)