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

io.gatling.core.feeder.FeederSource.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2011-2024 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.core.feeder

import java.nio.channels.FileChannel
import java.util.concurrent.ConcurrentHashMap

import scala.jdk.CollectionConverters._
import scala.util.Using

import io.gatling.core.config.GatlingConfiguration
import io.gatling.core.json.{ Json, JsonParsers }
import io.gatling.core.util._

import com.typesafe.scalalogging.LazyLogging

private[gatling] sealed trait FeederSource[T] {
  def feeder(options: FeederOptions[T], configuration: GatlingConfiguration): Feeder[Any]

  def name: String

  def recordsCount(options: FeederOptions[T], configuration: GatlingConfiguration): Int
}

private[gatling] final case class InMemoryFeederSource[T](records: IndexedSeq[Record[T]], override val name: String) extends FeederSource[T] with LazyLogging {
  require(records.nonEmpty, "Feeder must not be empty")

  override def feeder(options: FeederOptions[T], configuration: GatlingConfiguration): Feeder[Any] =
    InMemoryFeeder(records, options.conversion, options.strategy)

  override def recordsCount(options: FeederOptions[T], configuration: GatlingConfiguration): Int =
    records.length
}

private[feeder] object ZippedResourceCache {
  private val cache = new ConcurrentHashMap[Resource, Resource]()

  def unzipped(rawResource: Resource, unzip: Boolean): Resource =
    if (unzip) cache.computeIfAbsent(rawResource, Unzip.unzip) else rawResource
}

private[gatling] final class JsonFileFeederSource(resource: Resource, jsonParsers: JsonParsers) extends FeederSource[Any] {

  override def feeder(options: FeederOptions[Any], configuration: GatlingConfiguration): Feeder[Any] =
    Using.resource(ZippedResourceCache.unzipped(resource, options.unzip).inputStream) { is =>
      val node = jsonParsers.parse(is)
      require(node.isArray, "Root element of JSON feeder file isn't an array")

      val records = node.elements.asScala.collect {
        case node if node.isObject => Json.asScala(node).asInstanceOf[collection.immutable.Map[String, Any]]
      }.toVector
      InMemoryFeeder(records, options.conversion, options.strategy)
    }

  override def name: String = s"json(${resource.name})"

  override def recordsCount(options: FeederOptions[Any], configuration: GatlingConfiguration): Int =
    Using.resource(ZippedResourceCache.unzipped(resource, options.unzip).inputStream) { is =>
      Json.arrayOfObjectsLength(is, jsonParsers)
    }
}

private[gatling] final class SeparatedValuesFeederSource(val resource: Resource, separator: Char, quoteChar: Char) extends FeederSource[String] {
  override def feeder(options: FeederOptions[String], configuration: GatlingConfiguration): Feeder[Any] = {
    def applyBatch(res: Resource): Feeder[Any] = {
      val charset = configuration.core.charset
      options.loadingMode match {
        case Batch(bufferSize) =>
          BatchedSeparatedValuesFeeder(res.file, separator, quoteChar, options.conversion, options.strategy, bufferSize, charset)
        case Adaptive if res.file.length > configuration.core.feederAdaptiveLoadModeThreshold =>
          BatchedSeparatedValuesFeeder(res.file, separator, quoteChar, options.conversion, options.strategy, Batch.DefaultBufferLines, charset)
        case _ =>
          val records = Using.resource(FileChannel.open(res.file.toPath)) { channel =>
            SeparatedValuesParser.feederFactory(separator, quoteChar, charset)(channel).toVector
          }

          InMemoryFeeder(records, options.conversion, options.strategy)
      }
    }

    val uncompressedResource = ZippedResourceCache.unzipped(resource, options.unzip)
    applyBatch(uncompressedResource)
  }

  override def recordsCount(options: FeederOptions[String], configuration: GatlingConfiguration): Int = {
    val uncompressedResource = ZippedResourceCache.unzipped(resource, options.unzip)
    val linesIncludingHeader = Using.resource(uncompressedResource.inputStream)(LineCounter(configuration.core.charset).countLines)

    linesIncludingHeader - 1
  }

  override def name: String = s"csv(${resource.name})"
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy