1

I have a scenario in which I am starting multiple jmsSource (for different queues) using alpakka. I also need to detach the queues at any point in time. So I have added KillSwitch to the jms akka streams as below :-

trait MessageListener  {

  lazy val jmsPipeline = jmsSource
    .map { x => log.info(s"Received message ${x} from ${queue}"); x }
    .viaMat(KillSwitches.single)(Keep.right)
    .toMat(Sink.foreach { x => pipelineActorRef ! PreProcessorMessage(x) })
    (Keep.both)
    .run()

   def start(): Unit = {
             log.info("Invoking listener : {}", queue)
             jmsPipeline
             log.info("listener : {} started", queue)
          }
  def stop():Unit  =     jmsPipeline._1.shutdown()

  def queue: String

}

object ListenerA extends MessageListener {
  override def queue: String = "Queue_A"
}

object ListenerB extends MessageListener {
  override def queue: String = "Queue_B"
} 

.. and so on.

After I start up the application, all the queues are connected and works fine. But when I try to detach the queue using the stop method, not all the queues gets disconnected and the behaviour is random. I also checked that the killSwitch is different for all the listeners.

Could someone please tell me what's getting wrong here?

Jeffrey Chung
  • 19,319
  • 8
  • 34
  • 54
Kiras
  • 69
  • 6

1 Answers1

0

Your logs are supporting the illusion that you're connected to multiple queues with distinct streams, but you have multiple streams that are probably connected to the same queue. In both of your listener objects, the logger records the overridden queue name, but this queue name is not used to configure jmsSource.

You don't show the definition of jmsSource; apparently it's defined somewhere outside of the MessageListener trait, in which case both ListenerA and ListenerB are using the same jmsSource. In other words, while ListenerA and ListenerB have distinct instances of jmsPipeline (which is why the kill switches are different), both of these jmsPipeline instances are derived from the same jmsSource instance (unless jmsSource is a def that creates a different Source on each invocation, but even if this were the case, the basic problem remains: queue is not used in the configuration).

In Alpakka, the JMS queue is configured on JmsSourceSettings, so jmsSource probably looks something like the following:

val jmsSource: Source[String, NotUsed] = JmsSource.textSource(
  JmsSourceSettings(connectionFactory).withBufferSize(10).withQueue("MyQueue")
)                        // the queue is configured here ^

When ListenerA.start(), for example, is called, the following is logged:

Invoking listener : Queue_A
listener : Queue_A started

Again, "Queue_A" in the above log statements is the value of the overridden def queue: String member in ListenerA; it's not necessarily the queue that is actually configured in jmsSource ("MyQueue" in the above example). The same thing with ListenerB and with the messages that you're logging in the map combinator.

A straightforward fix is to move the definition of jmsSource and its JmsSourceSettings inside the MessageListener trait and actually use queue in those settings.

Jeffrey Chung
  • 19,319
  • 8
  • 34
  • 54