{"id":15050632,"url":"https://github.com/adzerk/apso","last_synced_at":"2025-07-20T19:31:53.383Z","repository":{"id":37431877,"uuid":"61729729","full_name":"adzerk/apso","owner":"adzerk","description":"Kevel's Scala utilities library","archived":false,"fork":false,"pushed_at":"2025-07-19T22:13:04.000Z","size":3380,"stargazers_count":27,"open_issues_count":1,"forks_count":5,"subscribers_count":25,"default_branch":"master","last_synced_at":"2025-07-20T00:52:47.340Z","etag":null,"topics":["audience","hacktoberfest","scala"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/adzerk.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2016-06-22T15:21:56.000Z","updated_at":"2025-07-19T22:13:08.000Z","dependencies_parsed_at":"2024-01-12T22:30:25.994Z","dependency_job_id":"58646477-7a44-4190-83cc-779e742d4864","html_url":"https://github.com/adzerk/apso","commit_stats":null,"previous_names":["velocidi/apso"],"tags_count":87,"template":false,"template_full_name":null,"purl":"pkg:github/adzerk/apso","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adzerk%2Fapso","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adzerk%2Fapso/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adzerk%2Fapso/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adzerk%2Fapso/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adzerk","download_url":"https://codeload.github.com/adzerk/apso/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adzerk%2Fapso/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266187025,"owners_count":23889898,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audience","hacktoberfest","scala"],"created_at":"2024-09-24T21:28:24.205Z","updated_at":"2025-07-20T19:31:53.376Z","avatar_url":"https://github.com/adzerk.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/adzerk/apso/master/apso.png\"/\u003e\u003c/p\u003e\n\n# Apso [![Build Status](https://github.com/adzerk/apso/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/adzerk/apso/actions/workflows/ci.yml?query=workflow%3ACI+branch%3Amaster) [![Maven Central](https://img.shields.io/maven-central/v/com.kevel/apso_2.13.svg)](https://maven-badges.herokuapp.com/maven-central/com.kevel/apso_2.13)\n\nApso is Kevel's collection of Scala utility libraries. It provides a series of useful methods.\n\n## Installation\n\nApso's latest release is built against Scala 2.13 and Scala 3.\n\nTo use it in an existing SBT project, add the following dependency to your `build.sbt`:\n\n```scala\nlibraryDependencies += \"com.kevel\" %% \"apso\" % \"0.24.1\"\n```\n\nThe project is divided in modules, you can instead install only a specific module.\n\nThe TestKit is available under the `apso-testkit` project. You can include it only for the `test` configuration:\n\n```scala\nlibraryDependencies += \"com.kevel\" %% \"apso-testkit\" % \"0.24.1\" % \"test\"\n```\n\nPlease take into account that the library is still in an experimental stage and the interfaces might change for subsequent releases.\n\n## Table of Contents\n\n- [Core](#core)\n    - [Config](#config)\n        - [LazyConfigFactory](#lazyconfigfactory)\n    - [HTTP](#http)\n    - [Geo](#geo)\n    - [Implicits](#implicits)\n    - [JreVersionHelper](#jreversionhelper)\n    - [ProgressBar](#progressbar)\n    - [Reflect](#reflect)\n    - [Retry](#retry)\n- [Pekko HTTP](#pekko-http)\n    - [ClientIPDirectives](#clientipdirectives)\n    - [ExtraMiscDirectives](#extramiscdirectives)\n    - [ProxySupport](#proxysupport)\n- [Amazon Web Services](#amazon-web-services)\n    - [ConfigCredentialsProvider](#configcredentialsprovider)\n    - [CredentialStore](#credentialstore)\n    - [S3Bucket](#s3bucket)\n    - [SerializableAWSCredentials](#serializableawscredentials)\n- [Caching](#caching)\n- [Collections](#collections)\n    - [Trie](#trie)\n    - [TypedMap](#typedmap)\n    - [Iterators](#iterators)\n        - [CircularIterator](#circulariterator)\n        - [MergedBufferedIterator](#mergedbufferediterator)\n- [Encryption](#encryption)\n- [Hashing](#hashing)\n- [IO](#io)\n    - [FileDescriptor](#filedescriptor)\n    - [ResourceUtil](#resourceutil)\n- [JSON](#json)\n- [Profiling](#profiling)\n    - [CpuSampler](#cpusampler)\n    - [SimpleJmx](#simplejmx)\n- [Time](#time)\n- [TestKit](#testkit)\n\n## Core\n\nTo use it in an existing SBT project, add the following dependency to your `build.sbt`:\n\n```scala\nlibraryDependencies += \"com.kevel\" %% \"apso-core\" % \"0.24.1\"\n```\n\n### Config\n\nApso provides methods to ease working with Typesafe's [config](https://github.com/typesafehub/config).\n\n#### LazyConfigFactory\n\nThe `LazyConfigFactory` object provides static methods for creating `Config` instances in a lazy way. The lazy way refers to the variable loading process. The usual process loads variables in config files eagerly (i.e. the path needs to be defined in the same file it is refered to). The loading process provided by `LazyConfigFactory` loads and merges all configuration files and only then resolves variables. This loading process introduces a third file (beyond the default ones - `application.conf` and `reference.conf`): `overrides.conf`. This file has priority over the `application.conf` file and can be used to specify keys that should always be overriden, e.g. by environment variables.\n\n### HTTP\n\nApso provides a tiny wrapper for [Dispatch](http://dispatch.databinder.net/) with synchronous operations. It's called `W`, and the following shows some sample usage:\n\n```scala\nimport com.kevel.apso.http.W\n\nW.get(\"http://www.google.com/\").getStatus\n// res0: Int = 302\n\nW.post(\"http://www.google.com/\", \"\").getStatus\n// res1: Int = 405\n\nW.put(\"http://www.google.com/\", \"\").getStatus\n// res2: Int = 405\n\nW.delete(\"http://www.google.com/\").getStatus\n// res3: Int = 405\n\nW.head(\"http://www.google.com/\").getStatus\n// res4: Int = 302\n```\n\nThe POST and PUT methods can also receive the body as `JSON` (of [circe](https://github.com/circe/circe)), which adds the `Content-type` header accordingly.\n\n### Geo\n\nThe `Geo` object provides methods to compute distances in kilometers between two points on the planet Earth, calculated using the spherical [law of cosines](https://en.wikipedia.org/wiki/Great-circle_distance#Formulas). Coordinates are represented by a pair of `Double` for latitude and longitude.\n\n```scala\nimport com.kevel.apso.Geo\n\nGeo.distance((41.1617609, -8.6024716), (41.1763745, -8.5964861))\n// res2: Double = 1.7004440788845807\n```\n\nYou can also have the distance function curried if you are computing distances from a fixed point:\n\n```scala\nval distFromOffice = Geo.distanceFrom((41.1617609, -8.6024716))\n```\n```scala\ndistFromOffice((41.1763745, -8.5964861))\n// res3: Double = 1.7004440788845807\n\ndistFromOffice((38.7223032, -9.1414664))\n// res4: Double = 275.118392477037\n```\n\n### Implicits\n\nApso provides implicit conversions from `String`, `Seq[_]`, `Map[_, _]`, `Seq[Map[_, _]]` and `AutoCloseable` to extended types that come packed with extended features.\n\n```scala\nimport com.kevel.apso.Implicits._\n\nSeq(1, 3, 5).mergeSorted(Seq(2, 4))\n// res6: Array[Int] = Array(1, 2, 3, 4, 5)\n\n(0 to 15).average\n// res7: Int = 7\n\nMap(1 -\u003e 2, 3 -\u003e 6).twoWayMerge(Map(2 -\u003e 4, 3 -\u003e 5)) { (a, b) =\u003e b }\n// res8: Map[Int, Int] = Map(2 -\u003e 4, 3 -\u003e 5, 1 -\u003e 2)\n\nMap(1 -\u003e 2, 2 -\u003e 4, 3 -\u003e 6).twoWayMerge(Map(2 -\u003e 2, 3 -\u003e 5)) { (a, b) =\u003e b }\n// res9: Map[Int, Int] = Map(2 -\u003e 2, 3 -\u003e 5, 1 -\u003e 2)\n\nMap(1 -\u003e 2, 2 -\u003e 3).mapKeys(_ + 1)\n// res10: Map[Int, Int] = Map(2 -\u003e 2, 3 -\u003e 3)\n```\n```scala\nval rand = new scala.util.Random(1)\n```\n```scala\nrand.choose((0 to 15).toSeq)\n// res11: Option[Int] = Some(value = 11)\n\nrand.choose((0 to 15).toSeq)\n// res12: Option[Int] = Some(value = 1)\n\nrand.choose((0 to 15).toSeq)\n// res13: Option[Int] = Some(value = 6)\n\nrand.choose((0 to 15).toSeq)\n// res14: Option[Int] = Some(value = 6)\n\nrand.chooseN((0 to 15).toSeq, 4)\n// res15: Seq[Int] = List(9, 8, 3, 0)\n\nrand.chooseN((0 to 15).toSeq, 4)\n// res16: Seq[Int] = List(7, 6, 5, 2)\n```\n\n### JreVersionHelper\n\nThe JreVersionHelper object provides helper methods to check the two most significant parts of the JRE version at runtime:\n\n```scala\nimport com.kevel.apso.JreVersionHelper\n\nJreVersionHelper.jreVersion\n// res0: (Int, Int) = (1, 8)\n```\n\n### ProgressBar\n\nThe `ProgressBar` represents a widget to print a dynamic progress bar in a console.\n\n```scala\nimport com.kevel.apso.ProgressBar\n\nval progress = ProgressBar(100)\n\nprogress.tick(1)\n// 1% [\u003e                                                     ] / [ 0.19 ] ops/s\n\nprogress.tick(2)\n// 3% [=\u003e                                                    ] - [ 0.15 ] ops/s\n\nprogress.tick(1)\n// 4% [==\u003e                                                   ] \\ [ 0.12 ] ops/s\n\nprogress.tick(10)\n// 14% [=======\u003e                                              ] | [ 0.31 ] ops/s\n\nprogress.tick(20)\n// 34% [==================\u003e                                   ] / [ 0.46 ] ops/s\n\nprogress.tick(30)\n// 64% [=================================\u003e                    ] - [ 0.77 ] ops/s\n```\n\n### Reflect\n\nThe `Reflect` object contains helpers for reflection-related tasks, namely to create an instance of a given class given its fully qualified name and also to access singleton objects:\n\n```scala\nscala\u003e import com.kevel.apso.Reflect\nimport com.kevel.apso.Reflect\n\nscala\u003e import com.kevel.apso.collection._\nimport com.kevel.apso.collection._\n\nscala\u003e Reflect.newInstance[HMap[Nothing]](\"com.kevel.apso.collection.HMap\")\nres0: com.kevel.apso.collection.HMap[Nothing] = HMap()\n\nscala\u003e Reflect.companion[Reflect.type](\"com.kevel.apso.Reflect\")\nres1: com.kevel.apso.Reflect.type = com.kevel.apso.Reflect$@3b1dbca\n```\n\n### Retry\n\nThe `Retry` object provides a method to retry methods or `Future`s a given number of times until they succeed or the specified maximum number of retries is reached:\n\n```scala\nimport scala.concurrent._\nimport scala.concurrent.duration._\nimport scala.concurrent.ExecutionContext.Implicits.global\n\nimport com.kevel.apso.Retry\n\nimport java.util.concurrent.atomic.AtomicInteger\n\nval a = new AtomicInteger()\n// a: AtomicInteger = 7\n\ndef f: Future[Int] = {\n  Future {\n    val value = a.getAndAdd(1)\n    if (value \u003e 5)\n      value\n    else {\n      throw new Exception()\n    }\n  }\n}\n\nAwait.result(Retry.retryFuture(10)(f), Duration.Inf)\n// res20: Int = 6\n\nvar attempts = 0\n// attempts: Int = 0\n\ndef m() = {\n  attempts += 1\n  if (attempts \u003e 5)\n    attempts\n  else\n    throw new Exception()\n}\n\nRetry.retry(10)(m())\n// res21: util.Try[Int] = Success(value = 6)\n```\n\n## Pekko HTTP\n\nThe `pekko-http` module provides additional [directives](https://pekko.apache.org/docs/pekko-http/current/routing-dsl/directives/) to be used in [pekko-http](https://pekko.apache.org/docs/pekko-http/current/).\n\nTo use it in an existing SBT project, add the following dependency to your `build.sbt`:\n\n```scala\nlibraryDependencies += \"com.kevel\" %% \"apso-pekko-http\" % \"0.24.1\"\n```\n\n### ClientIPDirectives\n\nThe `ClientIPDirectives` trait exposes an `optionalRawClientIP` directive that extracts the raw IP of the client from either the `X-Forwarded-For`, `Remote-Address` or `X-Real-IP` header, in that order of priority.\n\n### ExtraMiscDirectives\n\nThe `ExtraMiscDirectives` trait exposes the directives `cacheControlMaxAge(maxAgeDuration)` and `optionalRefererHost` to set the cache-control header to the supplied finite duration (the minimum resolution is 1 second) to extract the referer from the HTTP request header, respectively. The `ExtraMiscDirectives` companion object exposes a `cacheControlNoCache` directive to reply with the `no-cache` option in the `Cache-Control` header.\n\n### ProxySupport\n\nThe `ProxySupport` traits adds helper methods to proxy requests to a given uri, either directly (`proxyTo`), or with the unmatched path and query parameters of the current context (`proxyToUnmatchedPath`). In order for the client IP to be correctly propagated in `X-Forward-For` headers, the `ProxySupport` trait requires the `pekko.http.server.remote-address-attribute` setting to be `on`.\n\n## Amazon Web Services\n\nApso provides a group of classes to ease the interaction with the Amazon Web Services, namely S3 and EC2.\n\nTo use it in an existing SBT project, add the following dependency to your `build.sbt`:\n\n```scala\nlibraryDependencies += \"com.kevel\" %% \"apso-aws\" % \"0.24.1\"\n```\n\n### ConfigCredentialsProvider\n\nThe `ConfigCredentialsProvider` is an `AWSCredentialsProvider` (from AWS SDK for Java) that retrieves credentials from a typesafe configuration, allowing customization of its `Config` object, as well as the access key and secret key paths:\n\n```scala\nimport com.kevel.apso.aws._\n\nimport com.typesafe.config._\n\nval confProvider = ConfigCredentialsProvider(\n  config = ConfigFactory.parseString(\"\"\"{\n    aws {\n      access-key = \"\u003caccess-key\u003e\"\n      secret-key = \"\u003csecret-key\u003e\"\n    }\n  }\"\"\"),\n  accessKeyPath = \"aws.access-key\",\n  secretKeyPath = \"aws.secret-key\")\n\nval credentials = confProvider.getCredentials\n\ncredentials.getAWSAccessKeyId\n\ncredentials.getAWSSecretKey\n```\n\n### CredentialStore\n\nThe `CredentialStore` object serves as an endpoint for the retrieval of AWS credentials from available configurations. It extends the chain in the `DefaultAWSCredentialsProviderChain` (from AWS SDK for Java) with the retrieval of AWS credentials through the default typesafe configuration file (typically `application.conf`).\n\n### S3Bucket\n\nThe `S3Bucket` class wraps an instance of `AmazonS3Client` (from AWS SDK for Java) and exposes a higher level interface for pushing and pulling files to and from a bucket.\n\n### SerializableAWSCredentials\n\nThe `SerializableAWSCredentials` class provides a serializable container for AWS credentials, extending the `AWSCredentials` class (from AWS SDK for Java).\n\n## Caching\n\nThe `apso-caching` module provides utilities for caching, using `Caffeine` as the underlying implementation.\n\nTo use it in an existing SBT project, add the following dependency to your `build.sbt`:\n\n```scala\nlibraryDependencies += \"com.kevel\" %% \"apso-caching\" % \"0.24.1\"\n```\n\nThe simplest use case is bootstrapping a cache implementation based on a configuration object:\n\n```scala\nimport scala.concurrent.duration._\n\nimport com.kevel.apso.caching._\n\nval cache = config.Cache(Some(5.seconds), None).implementation[String, Int]\n// cache: com.github.blemale.scaffeine.Cache[String, Int]\n\nval x1 = cache.getIfPresent(\"requests\")\n// x1: Option[Int] = None\n\ncache.put(\"requests\", 1)\n\nval x2 = cache.getIfPresent(\"requests\")\n// x2: Option[Int] = Some(value = 1)\n```\n\nApso also provides utilities to simplify the caching of method calls. These utilities are provided as `cachedSync()` and\n`cachedAsync()` extension methods over all `FunctionN[]` types:\n\n```scala\nimport scala.concurrent._\nimport scala.concurrent.duration._\nimport scala.concurrent.ExecutionContext.Implicits.global\n\nimport java.util.concurrent.atomic.AtomicInteger\n\nimport com.kevel.apso.caching._\n\nval x = new AtomicInteger(0)\n// x: AtomicInteger = 2\n\nval cachedFn = ((i: Int) =\u003e {\n  val value = x.getAndAdd(i)\n  value\n}).cachedSync(config.Cache(Some(5.seconds), None))\n// cachedFn: SyncMemoizeFn1[Int, Int] = \u003cfunction1\u003e\n\ncachedFn(2)\n// res26: Int = 0\ncachedFn(2)\n// res27: Int = 0\nx\n// res28: AtomicInteger = 2\n\nval y = new AtomicInteger(0)\n// y: AtomicInteger = 3\n\nval cachedFutFn = ((i: Int) =\u003e Future {\n  val value = y.getAndAdd(i)\n  value\n}).cachedAsync(config.Cache(Some(5.seconds), None))\n// cachedFutFn: AsyncMemoizeFn1[Int, Int] = \u003cfunction1\u003e\n\nAwait.result(cachedFutFn(3), Duration.Inf)\n// res29: Int = 0\nAwait.result(cachedFutFn(3), Duration.Inf)\n// res30: Int = 0\ny\n// res31: AtomicInteger = 3\n```\n\n## Collections\n\nThe `apso-collections` module provides some helpful collections. To use it in an existing SBT project, add the following dependency to your `build.sbt`:\n\n```scala\nlibraryDependencies += \"com.kevel\" %% \"apso-collections\" % \"0.24.1\"\n```\n\n### Trie\n\nThe `Trie` class is an implementation of an immutable trie. An example usage follows:\n\n```scala\nimport com.kevel.apso.collection._\n\nval t = Trie[Char, Int]()\n// t: Trie[Char, Int] = Trie(value = None, nodes = Map())\n\nval nt = t.set(\"one\", 1).set(\"two\", 2).set(\"three\", 3).set(\"four\", 4)\n// nt: Trie[Char, Int] = Trie(\n//   value = None,\n//   nodes = Map(\n//     'o' -\u003e Trie(\n//       value = None,\n//       nodes = Map(\n//         'n' -\u003e Trie(\n//           value = None,\n//           nodes = Map('e' -\u003e Trie(value = Some(value = 1), nodes = Map()))\n//         )\n//       )\n//     ),\n//     't' -\u003e Trie(\n//       value = None,\n//       nodes = Map(\n//         'w' -\u003e Trie(\n//           value = None,\n//           nodes = Map('o' -\u003e Trie(value = Some(value = 2), nodes = Map()))\n//         ),\n//         'h' -\u003e Trie(\n//           value = None,\n//           nodes = Map(\n//             'r' -\u003e Trie(\n//               value = None,\n//               nodes = Map(\n//                 'e' -\u003e Trie(\n//                   value = None,\n//                   nodes = Map(\n//                     'e' -\u003e Trie(value = Some(value = 3), nodes = Map())\n//                   )\n//                 )\n//               )\n//             )\n//           )\n//         )\n//       )\n//     ),\n//     'f' -\u003e Trie(\n//       value = None,\n//       nodes = Map(\n//         'o' -\u003e Trie(\n//           value = None,\n//           nodes = Map(\n//             'u' -\u003e Trie(\n//               value = None,\n//               nodes = Map('r' -\u003e Trie(value = Some(value = 4), nodes = Map()))\n//             )\n//           )\n//         )\n// ...\n\nnt.get(\"one\")\n// res33: Option[Int] = Some(value = 1)\n\nnt.get(\"two\")\n// res34: Option[Int] = Some(value = 2)\n\nnt.get(\"five\")\n// res35: Option[Int] = None\n```\n\n### TypedMap\n\nThe `TypedMap` is a map that associates types with values. It can be used as follows:\n\n```scala\nimport com.kevel.apso.collection._\n\nval m = TypedMap(\"one\", 2, 3L)\n// m: TypedMap[Any] = Map(java.lang.String -\u003e one, Int -\u003e 2, Long -\u003e 3)\n\nm[String]\n// res37: String = \"one\"\n\nm[Int]\n// res38: Int = 2\n\nm[Long]\n// res39: Long = 3L\n\nm.get[String]\n// res40: Option[String] = Some(value = \"one\")\n\nm.get[Int]\n// res41: Option[Int] = Some(value = 2)\n\nm.get[Long]\n// res42: Option[Long] = Some(value = 3L)\n\nm.get[Char]\n// res43: Option[Char] = None\n```\n\n### Iterators\n\nApso provides some utility iterators.\n\n#### CircularIterator\n\nThe `CircularIterator` is an iterator that iterates over its elements in a circular way. See the following for sample usage:\n\n```scala\nimport com.kevel.apso.iterator.CircularIterator\n\nval circularIterator = CircularIterator(List(1, 2, 3).iterator)\n// circularIterator: CircularIterator[Int] = non-empty iterator\n\ncircularIterator.take(10).toList\n// res45: List[Int] = List(1, 2, 3, 1, 2, 3, 1, 2, 3, 1)\n```\n\n#### MergedBufferedIterator\n\nThe `MergedBufferedIterator` is a collection of sorted `BufferedIterators` that allows traversing them in order, while also providing a `mergeSorted` method to merge with another sorted `BufferedIterator`. See the following for sample usage:\n\n```scala\nimport com.kevel.apso.iterator.MergedBufferedIterator\n\nval it1 = MergedBufferedIterator(List(\n         (0 to 3).iterator.buffered,\n         (0 to 8).iterator.buffered,\n         (0 to 15).iterator.buffered,\n         (0 to 11).iterator.buffered))\n// it1: MergedBufferedIterator[Int] = empty iterator\n\nit1.toList\n// res47: List[Int] = List(\n//   0,\n//   0,\n//   0,\n//   0,\n//   1,\n//   1,\n//   1,\n//   1,\n//   2,\n//   2,\n//   2,\n//   2,\n//   3,\n//   3,\n//   3,\n//   3,\n//   4,\n//   4,\n//   4,\n//   5,\n//   5,\n//   5,\n//   6,\n//   6,\n//   6,\n//   7,\n//   7,\n//   7,\n//   8,\n//   8,\n//   8,\n//   9,\n//   9,\n//   10,\n//   10,\n//   11,\n//   11,\n//   12,\n//   13,\n//   14,\n//   15\n// )\n\nval it2 = MergedBufferedIterator(List(\n         Iterator(1, 3, 5).buffered,\n         Iterator(2).buffered))\n// it2: MergedBufferedIterator[Int] = non-empty iterator\n\nit2.mergeSorted(Iterator(4, 6).buffered).toList\n// res48: List[Int] = List(1, 2, 3, 4, 5, 6)\n```\n\n## Encryption\n\nApso provides some simple utility classes to deal with encryption and decryption of data, and methods that ease the\ncreation of the underlying Cyphers.\n\nTo use it in an existing SBT project, add the following dependency to your `build.sbt`:\n\n```scala\nlibraryDependencies += \"com.kevel\" %% \"apso-encryption\" % \"0.24.1\"\n```\n\nThe following shows the creation of `Encryptor` and `Decryptor` objects,\nby loading a `KeyStore` file holding a symmetric key, and its use to encrypt and\ndecrypt data:\n\n```scala\nimport com.kevel.apso.encryption._\n\nval encryptor = Encryptor(\"AES\", getClass.getResourceAsStream(\"/keystoreFile.jceks\"), \"keystorePass\", \"keyAlias\", \"keyPass\")\n\nval decryptor = Decryptor(\"AES\", getClass.getResourceAsStream(\"/keystoreFile.jceks\"), \"keystorePass\", \"keyAlias\", \"keyPass\")\n\nval secretData = \"secret_info\"\n\n// encrypt data and encode it in base64; then decrypt it to string\ndecryptor.get.decryptToString(encryptor.get.encryptToSafeString(secretData).get)\n```\n\n## Hashing\n\nApso provides utilities for various hashing functions. To use it in an existing SBT project, add the following dependency to your `build.sbt`:\n\n```scala\nlibraryDependencies += \"com.kevel\" %% \"apso-hashing\" % \"0.24.1\"\n```\n\n```scala\nimport com.kevel.apso.hashing.Implicits._\n\n\"abcd\".md5\n// res51: String = \"e2fc714c4727ee9395f324cd2e7f331f\"\n\n\"abcd\".murmurHash\n// res52: Long = 7785666560123423118L\n```\n\n## IO\n\nApso provides methods to deal with IO-related features in the `io` module.\n\nTo use it in an existing SBT project, add the following dependency to your `build.sbt`:\n\n```scala\nlibraryDependencies += \"com.kevel\" %% \"apso-io\" % \"0.24.1\"\n```\n\n### FileDescriptor\n\nApso introduces the concept of a `FileDescriptor`: a representation of a file stored in an arbitrary location. A descriptor includes logic to copy files to and from a local filesystem, as well as filesystem navigation logic. The following implementations of `FileDescriptor` are available:\n\n* LocalFileDescriptor (for files in the local filesystem);\n* S3FileDescriptor (for files in S3);\n* SftpFileDescriptor (for files served over SFTP).\n\n### ResourceUtil\n\nThe `ResourceUtil` object provides methods to access files available through Java's runtime environment classpath:\n\n```scala\nimport com.kevel.apso.io.ResourceUtil\n// import com.kevel.apso.io.ResourceUtil\n\nResourceUtil.getResourceURL(\"reference.conf\")\n// res0: String = /Users/jcazevedo/work/apso/apso/target/scala-2.11/classes/reference.conf\n\nResourceUtil.getResourceStream(\"reference.conf\")\n// res1: java.io.InputStream = java.io.BufferedInputStream@6f16d172\n\nResourceUtil.getResourceAsString(\"reference.conf\")\n// res2: String =\n// \"apso {\n//   io {\n//     file-descriptor {\n//       sftp.max-connections-per-host = 8\n//       sftp.max-idle-time = 10s\n//     }\n//   }\n// }\n// \"\n```\n\n## JSON\n\nApso includes a bunch of utilities to work with JSON serialization and deserialization, specifically with the [circe](https://circe.github.io/circe/) library. To use it in an existing SBT project, add the following dependency to your `build.sbt`:\n\n```scala\nlibraryDependencies += \"com.kevel\" %% \"apso-circe\" % \"0.24.1\"\n```\n\n### ExtraJsonProtocol\n\nThe `ExtraJsonProtocol` object combines three traits that provide extra `Encoders` and `Decoders` (of [circe](https://circe.github.io/circe/)) for some relevant types. The `Encoders` and `Decoders` are provided on each trait for the following types:\n\n* ExtraTimeJsonProtocol: `FiniteDuration`, `Interval` and `Period`;\n* ExtraHttpJsonProtocol: `URI`;\n* ExtraMiscJsonProtocol: `Config`, `DateTime`, `LocalDate` and `Currency`. It also includes the non-implicit methods `mapJsonArrayEncoder[K, V]` and `mapJsonArrayDecoder[K, V]` which serialize and deserialize a map as an array of key-value objects.\n\n### JSON\nThe `json` package provides some implicits around [circe](https://circe.github.io/circe/)'s `Json` to unwrap JSON values, merge two `Json` and create `Json` from a sequence of dot-separated paths with the corresponding leaf values. It also provides methods to access and delete fields on the `Json` object. See the following for sample usage:\n\n```scala\nimport com.kevel.apso.circe.Implicits._\nimport io.circe.syntax._\nimport io.circe.Json\n\n\"a\".asJson\n\"2\".asJson\nval js1 = Json.obj(\n  \"a\" := 2,\n  \"b\" := 3,\n  \"d\" := Json.obj(\"f\" := 6))\n\nval js2 = Json.obj(\n            \"c\" := 4,\n            \"d\" := Json.obj(\"e\" := 5))\n```\n```scala\njs1.deepMerge(js2).spaces2\n// res57: String = \"\"\"{\n//   \"c\" : 4,\n//   \"d\" : {\n//     \"e\" : 5,\n//     \"f\" : 6\n//   },\n//   \"a\" : 2,\n//   \"b\" : 3\n// }\"\"\"\n\nfromFullPaths(Seq(\n   \"a\" -\u003e 1.asJson,\n   \"b.c\" -\u003e 2.asJson,\n   \"b.d\" -\u003e 3.asJson,\n   \"e\" -\u003e \"xpto\".asJson,\n   \"f.g.h\" -\u003e 5.asJson)).spaces2\n// res58: String = \"\"\"{\n//   \"f\" : {\n//     \"g\" : {\n//       \"h\" : 5\n//     }\n//   },\n//   \"e\" : \"xpto\",\n//   \"b\" : {\n//     \"d\" : 3,\n//     \"c\" : 2\n//   },\n//   \"a\" : 1\n// }\"\"\"\n\njs1.getField[Int](\"a\")\n// res59: Option[Int] = Some(value = 2)\njs1.getField[Int](\"d.f\")\n// res60: Option[Int] = Some(value = 6)\njs1.getField[Int](\"x\")\n// res61: Option[Int] = None\n\njs1.deleteField(\"a\")\n// res62: Json = JObject(\n//   value = object[b -\u003e 3,d -\u003e {\n//   \"f\" : 6\n// }]\n// )\njs1.deleteField(\"d.f\")\n// res63: Json = JObject(\n//   value = object[a -\u003e 2,b -\u003e 3,d -\u003e {\n//   \n// }]\n// )\njs1.deleteField(\"x\")\n// res64: Json = JObject(\n//   value = object[a -\u003e 2,b -\u003e 3,d -\u003e {\n//   \"f\" : 6\n// }]\n// )\n```\n\n### JsonConvert\nThe `JsonConvert` object contains helpers for converting between JSON values and other structures. See the following for sample usage:\n\n```scala\nimport com.kevel.apso.circe._\n\nJsonConvert.toJson(\"abcd\")\n// res66: io.circe.Json = JString(value = \"abcd\")\n\nJsonConvert.toJson(1)\n// res67: io.circe.Json = JNumber(value = JsonLong(value = 1L))\n\nJsonConvert.toJson(Map(1 -\u003e 2, 3 -\u003e 4))\n// res68: io.circe.Json = JObject(value = object[1 -\u003e 2,3 -\u003e 4])\n```\n\n## Profiling\n\nThe `profiling` module of apso provides utilities to help with profiling the running process.\n\nTo use it in an existing SBT project, add the following dependency to your `build.sbt`:\n\n```scala\nlibraryDependencies += \"com.kevel\" %% \"apso-profiling\" % \"0.24.1\"\n```\n\n### CpuSampler\n\nThe `CpuSampler` is a lightweight configurable CPU profiler based on call stack sampling. When run as a thread, it periodically captures the call stacks of all live threads and maintains counters for each leaf method. The counters are then dumped to a logger with a given periodicity (most probably greater than the sampling period). Each data row written to the logger contains a timestamp, the method profiled, its location in the source code and the associated absolute counters and relative weight.\n\n### SimpleJmx\n\nThe `SimpleJmx` trait allows mixing in a simple JMX server. The JMX server is configured through a `Config` object, where the parameters `host` and `port` can be set. When behind a firewall, both the `port` defined (the RMI registry port) and the `port + 1` port (the RMI server port) need to be open. In the event of a binding failure to the defined port, a retry is performed with a random port.\n\n## Time\n\nThe `apso-time` module provides utilities to work with `DateTime` and `LocalDate`. It mainly adds support for better working with intervals.\n\nTo use it in an existing SBT project, add the following dependency to your `build.sbt`:\n\n```scala\nlibraryDependencies += \"com.kevel\" %% \"apso-time\" % \"0.24.1\"\n```\n\nSee the following sample usages:\n\n```scala\nimport org.joda.time.{DateTime, Period}\n\nimport com.kevel.apso.time._\n\nimport com.kevel.apso.time.Implicits._\n\n(new DateTime(\"2012-01-01\") to new DateTime(\"2012-01-01\")).toList\n// res70: List[DateTime] = List(2012-01-01T00:00:00.000Z)\n\n(new DateTime(\"2012-02-01\") until new DateTime(\"2012-03-01\") by Period.days(1))\n// res71: IterableInterval = IndexedSeq(\n//   2012-02-01T00:00:00.000Z,\n//   2012-02-02T00:00:00.000Z,\n//   2012-02-03T00:00:00.000Z,\n//   2012-02-04T00:00:00.000Z,\n//   2012-02-05T00:00:00.000Z,\n//   2012-02-06T00:00:00.000Z,\n//   2012-02-07T00:00:00.000Z,\n//   2012-02-08T00:00:00.000Z,\n//   2012-02-09T00:00:00.000Z,\n//   2012-02-10T00:00:00.000Z,\n//   2012-02-11T00:00:00.000Z,\n//   2012-02-12T00:00:00.000Z,\n//   2012-02-13T00:00:00.000Z,\n//   2012-02-14T00:00:00.000Z,\n//   2012-02-15T00:00:00.000Z,\n//   2012-02-16T00:00:00.000Z,\n//   2012-02-17T00:00:00.000Z,\n//   2012-02-18T00:00:00.000Z,\n//   2012-02-19T00:00:00.000Z,\n//   2012-02-20T00:00:00.000Z,\n//   2012-02-21T00:00:00.000Z,\n//   2012-02-22T00:00:00.000Z,\n//   2012-02-23T00:00:00.000Z,\n//   2012-02-24T00:00:00.000Z,\n//   2012-02-25T00:00:00.000Z,\n//   2012-02-26T00:00:00.000Z,\n//   2012-02-27T00:00:00.000Z,\n//   2012-02-28T00:00:00.000Z,\n//   2012-02-29T00:00:00.000Z\n// )\n\n(new DateTime(\"2012-01-01\") until new DateTime(\"2012-02-01\") by Period.minutes(2))\n// res72: IterableInterval = IndexedSeq(\n//   2012-01-01T00:00:00.000Z,\n//   2012-01-01T00:02:00.000Z,\n//   2012-01-01T00:04:00.000Z,\n//   2012-01-01T00:06:00.000Z,\n//   2012-01-01T00:08:00.000Z,\n//   2012-01-01T00:10:00.000Z,\n//   2012-01-01T00:12:00.000Z,\n//   2012-01-01T00:14:00.000Z,\n//   2012-01-01T00:16:00.000Z,\n//   2012-01-01T00:18:00.000Z,\n//   2012-01-01T00:20:00.000Z,\n//   2012-01-01T00:22:00.000Z,\n//   2012-01-01T00:24:00.000Z,\n//   2012-01-01T00:26:00.000Z,\n//   2012-01-01T00:28:00.000Z,\n//   2012-01-01T00:30:00.000Z,\n//   2012-01-01T00:32:00.000Z,\n//   2012-01-01T00:34:00.000Z,\n//   2012-01-01T00:36:00.000Z,\n//   2012-01-01T00:38:00.000Z,\n//   2012-01-01T00:40:00.000Z,\n//   2012-01-01T00:42:00.000Z,\n//   2012-01-01T00:44:00.000Z,\n//   2012-01-01T00:46:00.000Z,\n//   2012-01-01T00:48:00.000Z,\n//   2012-01-01T00:50:00.000Z,\n//   2012-01-01T00:52:00.000Z,\n//   2012-01-01T00:54:00.000Z,\n//   2012-01-01T00:56:00.000Z,\n//   2012-01-01T00:58:00.000Z,\n//   2012-01-01T01:00:00.000Z,\n//   2012-01-01T01:02:00.000Z,\n//   2012-01-01T01:04:00.000Z,\n//   2012-01-01T01:06:00.000Z,\n//   2012-01-01T01:08:00.000Z,\n//   2012-01-01T01:10:00.000Z,\n//   2012-01-01T01:12:00.000Z,\n//   2012-01-01T01:14:00.000Z,\n//   2012-01-01T01:16:00.000Z,\n//   2012-01-01T01:18:00.000Z,\n//   2012-01-01T01:20:00.000Z,\n//   2012-01-01T01:22:00.000Z,\n//   2012-01-01T01:24:00.000Z,\n//   2012-01-01T01:26:00.000Z,\n//   2012-01-01T01:28:00.000Z,\n//   2012-01-01T01:30:00.000Z,\n//   2012-01-01T01:32:00.000Z,\n//   2012-01-01T01:34:00.000Z,\n// ...\n```\n\n## TestKit\n\nApso comes with TestKits with extra useful matchers for [specs2](https://etorreborre.github.io/specs2/). The following traits with extra matchers are available:\n\n* `CustomMatchers`: provides a matcher to check if an object is serializable and one to check if a file exists;\n* `FutureExtraMatchers`: provides extra matchers for futures and implicit conversions for awaitables;\n* `JreVersionTestHelper`: provides a wrapper for `AsResult` to only run a spec if a specific JRE version is satisfied.\n\nTo use the version for version 4 of `specs2`, add the following dependency to your `build.sbt`:\n\n```scala\nlibraryDependencies += \"com.kevel\" %% \"apso-specs2_4\" % \"0.24.1\"\n```\n\nTo use the version for version 5 of `specs2` (only available for Scala 3), add the following dependency to your `build.sbt`:\n\n```scala\nlibraryDependencies += \"com.kevel\" %% \"apso-specs2_5\" % \"0.24.1\"\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadzerk%2Fapso","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadzerk%2Fapso","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadzerk%2Fapso/lists"}