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

io.gatling.jms.client.Tracker.scala Maven / Gradle / Ivy

There is a newer version: 3.13.1
Show newest version
/*
 * Copyright 2011-2019 GatlingCorp (https://gatling.io)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.gatling.jms.client

import java.util.{ HashMap => JHashMap }
import javax.jms.Message

import scala.collection.mutable
import scala.concurrent.duration._

import io.gatling.commons.stats.{ KO, OK, Status }
import io.gatling.commons.util.Clock
import io.gatling.commons.validation.Failure
import io.gatling.core.action.Action
import io.gatling.core.akka.BaseActor
import io.gatling.core.check.Check
import io.gatling.core.config.GatlingConfiguration
import io.gatling.core.session.Session
import io.gatling.core.stats.StatsEngine
import io.gatling.jms._

import akka.actor.{ Props, Timers }

/**
 * Advise actor a message was sent to JMS provider
 */
final case class MessageSent(
    matchId: String,
    sent: Long,
    replyTimeout: Long,
    checks: List[JmsCheck],
    session: Session,
    next: Action,
    requestName: String
)

/**
 * Advise actor a response message was received from JMS provider
 */
final case class MessageReceived(
    matchId: String,
    received: Long,
    message: Message
)

case object TimeoutScan

object Tracker {
  def props(statsEngine: StatsEngine, clock: Clock, configuration: GatlingConfiguration) =
    Props(new Tracker(statsEngine, clock, configuration.jms.replyTimeoutScanPeriod))
}

/**
 * Bookkeeping actor to correlate request and response JMS messages
 * Once a message is correlated, it publishes to the Gatling core DataWriter
 */
class Tracker(statsEngine: StatsEngine, clock: Clock, replyTimeoutScanPeriod: FiniteDuration) extends BaseActor with Timers {

  private val sentMessages = mutable.HashMap.empty[String, MessageSent]
  private val timedOutMessages = mutable.ArrayBuffer.empty[MessageSent]
  private var periodicTimeoutScanTriggered = false

  def triggerPeriodicTimeoutScan(): Unit =
    if (!periodicTimeoutScanTriggered) {
      periodicTimeoutScanTriggered = true
      timers.startPeriodicTimer("timeoutTimer", TimeoutScan, replyTimeoutScanPeriod)
    }

  override def receive: Receive = {
    // message was sent; add the timestamps to the map
    case messageSent: MessageSent =>
      sentMessages += messageSent.matchId -> messageSent
      if (messageSent.replyTimeout > 0) {
        triggerPeriodicTimeoutScan()
      }

    // message was received; publish stats and remove from the hashmap
    case MessageReceived(matchId, received, message) =>
      // if key is missing, message was already acked and is a dup, or request timedout
      sentMessages.remove(matchId).foreach {
        case MessageSent(_, sent, _, checks, session, next, requestName) =>
          processMessage(session, sent, received, checks, message, next, requestName)
      }

    case TimeoutScan =>
      val now = clock.nowMillis
      sentMessages.valuesIterator.foreach { message =>
        val replyTimeout = message.replyTimeout
        if (replyTimeout > 0 && (now - message.sent) > replyTimeout) {
          timedOutMessages += message
        }
      }

      for (MessageSent(matchId, sent, receivedTimeout, _, session, next, requestName) <- timedOutMessages) {
        sentMessages.remove(matchId)
        executeNext(session.markAsFailed, sent, now, KO, next, requestName, Some(s"Reply timeout after $receivedTimeout ms"))
      }
      timedOutMessages.clear()
  }

  private def executeNext(
      session: Session,
      sent: Long,
      received: Long,
      status: Status,
      next: Action,
      requestName: String,
      message: Option[String]
  ): Unit = {
    statsEngine.logResponse(session, requestName, sent, received, status, None, message)
    next ! session.logGroupRequestTimings(sent, received).increaseDrift(clock.nowMillis - received)
  }

  /**
   * Processes a matched message
   */
  private def processMessage(
      session: Session,
      sent: Long,
      received: Long,
      checks: List[JmsCheck],
      message: Message,
      next: Action,
      requestName: String
  ): Unit = {
    // run all the checks, advise the Gatling API that it is complete and move to next
    val (newSession, error) = Check.check(message, session, checks, new JHashMap(2))
    error match {
      case Some(Failure(errorMessage)) => executeNext(newSession.markAsFailed, sent, received, KO, next, requestName, Some(errorMessage))
      case _                           => executeNext(newSession, sent, received, OK, next, requestName, None)
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy