All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jms4s.JmsProducer.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2020 Functional Programming in Bologna
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package jms4s

import cats.data.NonEmptyList
import cats.effect._
import cats.effect.std.Queue
import cats.syntax.all._
import jms4s.config.DestinationName
import jms4s.jms._
import jms4s.model.SessionType

import scala.concurrent.duration.FiniteDuration

trait JmsProducer[F[_]] {

  def sendN(
    messageFactory: MessageFactory[F] => F[NonEmptyList[(JmsMessage, DestinationName)]]
  ): F[Unit]

  def sendNWithDelay(
    messageFactory: MessageFactory[F] => F[NonEmptyList[(JmsMessage, (DestinationName, Option[FiniteDuration]))]]
  ): F[Unit]

  def sendWithDelay(
    messageFactory: MessageFactory[F] => F[(JmsMessage, (DestinationName, Option[FiniteDuration]))]
  ): F[Unit]

  def send(messageFactory: MessageFactory[F] => F[(JmsMessage, DestinationName)]): F[Unit]
}

private[jms4s] class ContextPool[F[_]: Sync](private val contextsPool: Queue[F, (JmsContext[F], MessageFactory[F])]) {

  def acquireAndUseContext[A](f: (JmsContext[F], MessageFactory[F]) => F[A]): F[A] =
    MonadCancel[F].bracket(contextsPool.take) {
      case (ctx, mf) => f(ctx, mf)
    }(usedCtx => contextsPool.offer(usedCtx))
}

object ContextPool {

  def create[F[_]: Async](context: JmsContext[F], concurrencyLevel: Int): Resource[F, ContextPool[F]] =
    for {
      pool <- Resource.eval(
               Queue.bounded[F, (JmsContext[F], MessageFactory[F])](concurrencyLevel)
             )
      _ <- (0 until concurrencyLevel).toList.traverse_ { _ =>
            for {
              ctx <- context.createContext(SessionType.AutoAcknowledge)
              mf  = MessageFactory[F](ctx)
              _   <- Resource.eval(pool.offer((ctx, mf)))
            } yield ()
          }
    } yield new ContextPool(pool)
}

object JmsProducer {

  private[jms4s] def make[F[_]: Async](
    context: JmsContext[F],
    concurrencyLevel: Int
  ): Resource[F, JmsProducer[F]] =
    for {
      pool <- ContextPool.create(context, concurrencyLevel)
    } yield new JmsProducer[F] {

      override def sendN(
        f: MessageFactory[F] => F[NonEmptyList[(JmsMessage, DestinationName)]]
      ): F[Unit] =
        pool.acquireAndUseContext {
          case (ctx, mf) =>
            for {
              messagesWithDestinations <- f(mf)
              _ <- messagesWithDestinations.traverse_ {
                    case (message, destinationName) => ctx.send(destinationName, message)
                  }
            } yield ()
        }

      override def sendNWithDelay(
        f: MessageFactory[F] => F[NonEmptyList[(JmsMessage, (DestinationName, Option[FiniteDuration]))]]
      ): F[Unit] =
        pool.acquireAndUseContext {
          case (ctx, mf) =>
            for {
              messagesWithDestinationsAndDelayes <- f(mf)
              _ <- messagesWithDestinationsAndDelayes.traverse_ {
                    case (message, (destinatioName, duration)) =>
                      duration.fold(ctx.send(destinatioName, message))(delay =>
                        ctx.send(destinatioName, message, delay)
                      )
                  }

            } yield ()
        }

      override def sendWithDelay(
        f: MessageFactory[F] => F[(JmsMessage, (DestinationName, Option[FiniteDuration]))]
      ): F[Unit] =
        pool.acquireAndUseContext {
          case (ctx, mf) =>
            for {
              (message, (destinationName, delay)) <- f(mf)
              _                                   <- delay.fold(ctx.send(destinationName, message))(delay => ctx.send(destinationName, message, delay))
            } yield ()
        }

      override def send(f: MessageFactory[F] => F[(JmsMessage, DestinationName)]): F[Unit] =
        pool.acquireAndUseContext {
          case (ctx, mf) =>
            for {
              (message, destination) <- f(mf)
              _                      <- ctx.send(destination, message)
            } yield ()
        }

    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy