{"id":19468804,"url":"https://github.com/imrafaelmerino/jio","last_synced_at":"2025-08-08T08:46:27.309Z","repository":{"id":264387906,"uuid":"848195410","full_name":"imrafaelmerino/jio","owner":"imrafaelmerino","description":"Jio is a powerful Java library designed to simplify and enhance input/output operations by leveraging the power of expressions and functions","archived":false,"fork":false,"pushed_at":"2024-11-23T21:51:18.000Z","size":5924,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-08T04:51:16.576Z","etag":null,"topics":["functional-programming","java","property-based-testing","virtual-threads"],"latest_commit_sha":null,"homepage":"","language":"Java","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/imrafaelmerino.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-08-27T10:07:30.000Z","updated_at":"2024-11-23T21:51:22.000Z","dependencies_parsed_at":null,"dependency_job_id":"9f17adbe-0c3c-4823-b819-a38bb48f362d","html_url":"https://github.com/imrafaelmerino/jio","commit_stats":null,"previous_names":["imrafaelmerino/jio"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/imrafaelmerino%2Fjio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/imrafaelmerino%2Fjio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/imrafaelmerino%2Fjio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/imrafaelmerino%2Fjio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/imrafaelmerino","download_url":"https://codeload.github.com/imrafaelmerino/jio/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240682911,"owners_count":19840594,"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":["functional-programming","java","property-based-testing","virtual-threads"],"created_at":"2024-11-10T18:43:56.950Z","updated_at":"2025-02-25T14:27:16.625Z","avatar_url":"https://github.com/imrafaelmerino.png","language":"Java","funding_links":["https://www.buymeacoffee.com/imrafaelmerino"],"categories":[],"sub_categories":[],"readme":"\u003cimg src=\"logo/package_twitter_itsywb76/black/full/coverphoto/black_logo_white_background.png\" alt=\"logo\"/\u003e\n\n[![Buy Me a Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-%E2%98%95%20Support-yellow)](https://www.buymeacoffee.com/imrafaelmerino)\n\n- [Code wins arguments](#cwa)\n- [Introduction](#Introduction)\n- [jio-exp](#jio-exp)\n  - [Creating effects](#creating-effects)\n  - [Lambdas](#lambdas)\n  - [Operations with effects](#operations-with-effects)\n  - [Expressions](#expressions)\n  - [Clocks](#Clocks)\n  - [Debugging and JFR integration](#Debugging-and-JFR-integration)\n  - [Installation](#exp-installation)\n- [jio-http](#jio-http)\n  - [HTTP server](#httpserver)\n  - [HTTP client](#httpclient)\n  - [OAUTH HTTP client](#oauth)\n  - [Installation](#http-installation)\n- [jio-test](#jio-test)\n  - [Junit integration](#junit)\n  - [Stubs](#stubs)\n    - [IO stubs](#iostubs)\n    - [Clock stubs](#clockstubs)\n    - [Http Server Stubs](#httpserverstubs)\n  - [Property based testing](#pbt)\n  - [Installation](#test-installation)\n- [jio-mongodb](#jio-mongodb)\n  - [MongoLambda](#monglambda)\n  - [API](#jio-mongodb-gs)\n    - [Find Operations](#find-operations)\n    - [Insert Operations](#insert-operations)\n    - [Delete Operations](#delete-operations)\n    - [Update and Replace Operations](#update-and-replace-operations)\n    - [Count](#count)\n    - [FindAndXXX operations](#findoneandxxx-operations)\n    - [Aggregate](#aggregate)\n    - [Watcher](#watcher)\n    - [Specifying an Executor](#mongo-executors)\n    - [Configuring options](#mongo-options)\n  - [Transactions](#transactions)\n  - [Common exceptions](#common-exceptions)\n  - [Debugging and JFR integration](#mongo-Debugging-and-JFR-integration)\n  - [Installation](#mongo-installation)\n- [jio-cli](#jio-cli)\n- [jio-jdbc](#jio-jdbc)\n\n## \u003ca name=\"cwa\"\u003e\u003ca/\u003e Code wins arguments\n\nI think the age-old \"Hello world\" example has outlived its usefulness. While it once served as a\nfoundational teaching tool, its simplicity no longer suffices in today's world. In the current\nlandscape, where real-world scenarios are markedly more intricate, I present a \"Hello world\" example\nthat truly mirrors the complexity of modern development.\n\n### Signup Service specification\n\nLet's jump into the implementation of a signup service with the following requirements:\n\n1. The signup service takes a JSON input containing at least two fields: email and address, both\n   expected as strings. The service's first step is to validate and standardize the address using\n   the Google Geocode API. The results obtained from Google are then presented to the frontend for\n   user selection or rejection.\n2. In addition to address validation, the service stores the client's information in a MongoDB\n   database. The MongoDB identifier returned becomes the client identifier, which must be sent back\n   to the frontend. If the client is successfully saved in the database and the user doesn't exist\n   in an LDAP system, two additional actions occur:\n\n- The user is sent to the LDAP service.\n- If the previous operation succeeds, an activation email is sent to the user.\n\n3. The signup service also provides information about the total number of existing clients in the\n   MongoDB database. This information can be utilized by the frontend to display a welcoming message\n   to the user, such as \"You're the user number 3000!\" If an error occurs the service returns -1,\n   and the frontend will not display the message.\n4. Crucially, the signup service is designed to perform all these operations in parallel. This\n   includes the request to Google for address validation and the MongoDB operations, which encompass\n   both data persistence and counting.\n\n### Response Structure\n\nThe response from the signup service follows this structure:\n\n```code\n{\n  \"number_users\": integer,\n  \"id\": string,\n  \"addresses\": array\n}\n```\n\n### Signup Service implementation\n\nThe `SignupService` orchestrates all the operations with elegance and efficiency. This service is\nconstructed with a set of [lambdas](#lambdas), where a lambda is essentially a function that takes\nan input and produces an output. Unlike traditional functions, lambdas don't throw exceptions;\ninstead, they gracefully return exceptions as regular values.\n\n```java\nimport jio.*;\nimport jio.time.Clock;\nimport jsonvalues.*;\n\nimport java.time.Instant;\n\nimport static java.util.Objects.requireNonNull;\n\npublic class SignupService implements Lambda\u003cJsObj, JsObj\u003e {\n\n  Lambda\u003cJsObj, Void\u003e persistLDAP;\n  Lambda\u003cString, JsArray\u003e normalizeAddress;\n  IO\u003cInteger\u003e countUsers;\n  Lambda\u003cJsObj, String\u003e persistMongo;\n  Lambda\u003cJsObj, Void\u003e sendEmail;\n  Lambda\u003cString, Boolean\u003e existsInLDAP;\n\n  //constructor\n\n  @Override\n  public IO\u003cJsObj\u003e apply(JsObj user) {\n\n    String email = user.getStr(\"email\");\n    String address = user.getStr(\"address\");\n    String context = \"signup\";\n\n    Lambda\u003cString, String\u003e LDAPFlow =\n        id -\u003e IfElseExp.\u003cString\u003epredicate(existsInLDAP.apply(email))\n                       .consequence(() -\u003e IO.succeed(id))\n                       .alternative(() -\u003e PairExp.seq(persistLDAP.apply(user),\n                                                      sendEmail.apply(user)\n                                                     )\n                                                 .debugEach(context)\n                                                 .map(n -\u003e id)\n                                   )\n                       .debugEach(context);\n\n    return JsObjExp.par(\"number_users\",\n                        countUsers.recover(exc -\u003e -1)\n                                  .map(JsInt::of),\n\n                        \"id\",\n                        persistMongo.then(LDAPFlow)\n                                    .apply(user)\n                                    .map(JsStr::of),\n\n                        \"addresses\",\n                        normalizeAddress.apply(address)\n                       )\n                   .debugEach(context);\n  }\n}\n\n```\n\nNoteworthy points:\n\n- **JsObjExp**: The `JsObjExp` expression is highly expressive. It allows us to define the structure\n  of the resulting JSON object in a clear and declarative manner. In our code, we use it to\n  construct a JSON object with multiple key-value pairs, each representing a specific piece of\n  information (`\"number_users\"`, `\"id\"`, `\"addresses\"`, `\"timestamp\"`, etc.). This approach\n  simplifies the creation of complex JSON structures and enhances code readability.\n\n- Error handling is handled gracefully with the `recover` functions, providing alternative values\n  (e.g., -1 for `countUsers`) in case of errors.\n\n- **IfElseExp**: The `IfElseExp` expression is a clear and concise way to handle conditional logic.\n  It enables us to specify the consequence and alternative branches based on a predicate\n  (`existsInLDAP.apply(email)` in this case). This expressiveness makes it evident that if the user\n  exists in LDAP, we succeed with an ID, otherwise, we perform a sequence of operations using\n  `PairExp`. It enhances the readability of the code, making it easy to understand the branching\n  logic.\n\n- **PairExp**: The `PairExp` expression streamlines the execution of two effects, either\n  sequentially or in parallel, and then combines their results into a pair. In this scenario, we\n  utilize `PairExp.seq` to execute the `persistLDAP` and `sendEmail` operations sequentially.\n  However, it's essential to emphasize that in this particular example, our primary concern is the\n  successful completion of both operations. Therefore, in the absence of any failures, the result\n  will be a pair containing two `null` values: (null, null), as both operations return `Void`.\n\n- **debugEach**: Debugging plays a pivotal role in software development, and real-world applications\n  often handle a multitude of messages from various users and requests. When issues arise,\n  identifying which log events are pertinent to the problem can be challenging, particularly in\n  high-traffic scenarios. JIO streamlines the debugging process and enhances contextual logging\n  through its `debug` and `debugEach` methods.\n\n- **JFR (Java Flight Recorder)**: JIO leverages JFR for logging and debugging purposes. This choice\n  offers several advantages. First, it's Java-native, which means it seamlessly integrates with the\n  Java ecosystem, ensuring compatibility and performance. Second, it avoids the complexities and\n  potential conflicts associated with using external logging libraries, of which there are many in\n  the Java landscape. By relying on JFR, we maintain a lightweight and efficient approach to logging\n  that is both reliable and highly effective.\n\n- Last but not least, the backbone of JIO is the `IO` class that we'll explore in detail in the next\n  section.\n\n### Testing the Signup Service with JIO\n\nJIO offers an elegant and efficient approach to testing. It eliminates the need for external\nlibraries like Mockito, making your testing experience smoother and more expressive. Since Lambdas\nare just functions, you can implement them in your test class, directly. This approach enables you\nto tailor the behavior of each lambda to your specific test scenario, making your tests highly\nadaptable and expressive:\n\n```java\n\n\npublic class SignupTests {\n\n  @RegisterExtension\n  static Debugger debugger = Debugger.of(Duration.ofSeconds(2));\n\n  @Test\n  public void test() {\n\n    Lambda\u003cJsObj, Void\u003e persistLDAP = _ -\u003e IO.NULL();\n\n    Lambda\u003cString, JsArray\u003e normalizeAddress =\n        _ -\u003e IO.succeed(JsArray.of(\"address1\",\n                                   \"address2\"));\n\n    IO\u003cInteger\u003e countUsers =\n        IO.lazy(() -\u003e ThreadLocalRandom.current()\n                                       .nextInt(0,\n                                                10));\n\n    Lambda\u003cJsObj, String\u003e persistMongo = _ -\u003e IO.succeed(\"id\");\n\n    Lambda\u003cJsObj, Void\u003e sendEmail = _ -\u003e IO.NULL();\n\n    Lambda\u003cString, Boolean\u003e existsInLDAP = _ -\u003e IO.FALSE;\n\n    JsObj user = JsObj.of(\"email\",\n                          JsStr.of(\"imrafaelmerino@gmail.com\"),\n                          \"address\",\n                          JsStr.of(\"Elm's Street\")\n                         );\n\n    JsObj resp = new SignupService(persistLDAP,\n                                   normalizeAddress,\n                                   countUsers,\n                                   persistMongo,\n                                   sendEmail,\n                                   existsInLDAP\n    )\n        .apply(user)  //returns an IO effect, nothing is computed\n        .compute()    //computes the effect a return a Result (either Success of Failure)\n        .getOutput(); //get the sucessful output or throws RuntimeException\n\n    Assertions.assertTrue(resp.containsKey(\"number_users\"));\n\n  }\n\n}\n\n```\n\n### Debugging with the Debugger Extension\n\nWhen it comes to debugging your code during testing, having access to detailed information is\ninvaluable. JIO's Debugger extension simplifies this process by creating an event stream for a\nspecified duration and printing all the events sent to the Java Flight Recorder (JFR) system during\nthat period.\n\nHere's a breakdown of how it works:\n\n1. **Debugger Extension Registration**: In your test class, you register the Debugger JUnit\n   extension using the `@RegisterExtension` annotation. You specify the duration for which the\n   debugger captures events.\n\n2. **Using `debug` and `debugEach`**: Within your code, you utilize the `debug` and `debugEach`\n   methods provided by JIO. These methods allow you to send events to the JFR system after a value\n   or expression is evaluated.\n\n3. **Event Printing**: During the execution of the test for the specified duration, the Debugger\n   extension prints out all the events that were sent to the JFR system. These events include\n   information about the expressions being evaluated, their results, execution durations, contextual\n   data, and more.\n\n4. **Stream Ordering**: Importantly, the event stream is ordered. Events are printed in the order in\n   which they occurred, providing a clear chronological view of your code's execution.\n\n5. **Pinpointing Bugs and Issues**: With the event stream and detailed logs in hand, you can easily\n   pinpoint any bugs, unexpected behavior, or performance bottlenecks.\n\nIn summary, the Debugger extension in JIO transforms the testing and debugging process into a\nstreamlined and informative experience with minimal effort from developers. It empowers developers\nto gain deep insights into their code's behavior without relying on external logging libraries or\ncomplex setups.\n\nFind below all the events that are printed out during the execution of the previous JUnit test.\n\n```text\n\nStarted JFR stream for 2,000 sg in SignupTests\n\n------ eval-exp --------\n|  Expression: count_number_users\n|  Result: SUCCESS\n|  Duration: 69,833 µs\n|  Output: 3\n|  Context: signup\n|  Thread: main\n|  Event Start Time: 2024-02-13T10:08:09.920563792+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: JsObjExpPar[number_users]\n|  Result: SUCCESS\n|  Duration: 1,418 ms\n|  Output: 3\n|  Context: signup\n|  Thread: main\n|  Event Start Time: 2024-02-13T10:08:09.91973025+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: JsObjExpPar[addresses]\n|  Result: SUCCESS\n|  Duration: 4,583 µs\n|  Output: [\"address1\",\"address2\"]\n|  Context: signup\n|  Thread: main\n|  Event Start Time: 2024-02-13T10:08:09.921166292+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: IfElseExp-predicate\n|  Result: SUCCESS\n|  Duration: 5,958 µs\n|  Output: false\n|  Context: signup\n|  Thread: main\n|  Event Start Time: 2024-02-13T10:08:09.924032792+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: PairExpSeq[1]\n|  Result: SUCCESS\n|  Duration: 4,709 µs\n|  Output: null\n|  Context: signup\n|  Thread: main\n|  Event Start Time: 2024-02-13T10:08:09.924848208+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: PairExpSeq[2]\n|  Result: SUCCESS\n|  Duration: 4,291 µs\n|  Output: null\n|  Context: signup\n|  Thread: main\n|  Event Start Time: 2024-02-13T10:08:09.924969417+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: PairExpSeq\n|  Result: SUCCESS\n|  Duration: 284,875 µs\n|  Output: (null, null)\n|  Context: signup\n|  Thread: main\n|  Event Start Time: 2024-02-13T10:08:09.924846917+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: IfElseExp-alternative\n|  Result: SUCCESS\n|  Duration: 2,744 ms\n|  Output: id\n|  Context: signup\n|  Thread: main\n|  Event Start Time: 2024-02-13T10:08:09.924842+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: IfElseExp\n|  Result: SUCCESS\n|  Duration: 3,568 ms\n|  Output: id\n|  Context: signup\n|  Thread: main\n|  Event Start Time: 2024-02-13T10:08:09.924030958+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: JsObjExpPar[id]\n|  Result: SUCCESS\n|  Duration: 4,058 ms\n|  Output: id\n|  Context: signup\n|  Thread: main\n|  Event Start Time: 2024-02-13T10:08:09.923546958+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: JsObjExpPar[timestamp]\n|  Result: SUCCESS\n|  Duration: 219,208 µs\n|  Output: 2024-02-13T09:08:09.927Z\n|  Context: signup\n|  Thread: main\n|  Event Start Time: 2024-02-13T10:08:09.927616125+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: JsObjExpPar\n|  Result: SUCCESS\n|  Duration: 8,925 ms\n|  Output: {\"addresses\":[\"address1\",\"address2\"],\"number_users\":3,\"timestamp\":\"2024-02-13T09:08:09.927Z\",\"id\":\"id\"}\n|  Context: signup\n|  Thread: main\n|  Event Start Time: 2024-02-13T10:08:09.919238167+01:00\n-------------------------\n\n```\n\nThe displayed events are self-explanatory. If you're wondering whether this is the actual format of\nevents, the answer is yes. When testing, it's preferable to opt for a format that's easy to read and\ncomprehend, rather than cramming all the information into a single line.\n\nIn summary, these traces are like breadcrumbs that guide you through your code, making testing and\ndebugging more efficient and effective. They enable you to pinpoint issues, optimize performance,\nand gain a deeper understanding of how your code behaves during testing.\n\nIn the previous example, you may have observed that all evaluations were performed by the main\nthread. This is because the IO effects returned by the lambdas were essentially constants, and no\nspecific `Executor` was defined. Even if an `Executor` were specified, there are cases where the\nCompletableFuture framework, heavily relied upon by JIO, may choose not to switch contexts between\nthreads if it deems it unnecessary.\n\nHowever, you can introduce random delays and leverage virtual threads to create a more realistic\nexample. To achieve this, more complex stubs are used from the `jio-test` library through the\n`StubBuilder` class. These stubs allow you to specify generators for their creation, ensuring\ndifferent values are returned every time. Here's how you can utilize them:\n\n```java\n\n@Test\npublic void test() {\n\n  Gen\u003cDuration\u003e delayGen = IntGen.arbitrary(0,\n                                            200)\n                                 .map(Duration::ofMillis);\n\n  IO\u003cInteger\u003e countUsers =\n      StubBuilder.ofSucGen(IntGen.arbitrary(0,\n                                            100000))\n                 .withDelays(delayGen)\n                 .withExecutor(Executors.newVirtualThreadPerTaskExecutor())\n                 .get();\n\n  Lambda\u003cJsObj, String\u003e persistMongo =\n      _ -\u003e StubBuilder.ofSucGen(StrGen.alphabetic(20,\n                                                  20))\n                      .withDelays(delayGen)\n                      .withExecutor(Executors.newVirtualThreadPerTaskExecutor())\n                      .get();\n\n  Lambda\u003cJsObj, Void\u003e sendEmail =\n      _ -\u003e StubBuilder.\u003cVoid\u003eofSucGen(Gen.cons(null))\n                      .withDelays(delayGen)\n                      .withExecutor(Executors.newVirtualThreadPerTaskExecutor())\n                      .get();\n\n  Lambda\u003cString, Boolean\u003e existsInLDAP =\n      _ -\u003e StubBuilder.ofSucGen(BoolGen.arbitrary())\n                      .withDelays(delayGen)\n                      .withExecutor(Executors.newVirtualThreadPerTaskExecutor())\n                      .get();\n\n  Lambda\u003cJsObj, Void\u003e persistLDAP =\n      _ -\u003e StubBuilder.\u003cVoid\u003eofSucGen(Gen.cons(null))\n                      .withDelays(delayGen)\n                      .withExecutor(Executors.newVirtualThreadPerTaskExecutor())\n                      .get();\n\n  Lambda\u003cString, JsArray\u003e normalizeAddresses =\n      _ -\u003e StubBuilder.ofSucGen(JsArrayGen.ofN(JsStrGen.alphabetic(),\n                                               3))\n                      .withDelays(delayGen)\n                      .withExecutor(Executors.newVirtualThreadPerTaskExecutor())\n                      .get();\n}\n```\n\nThese `StubBuilder` instances are essentially builders that create IO stubs. They allow you to\nintroduce variability and randomness into your tests, making them more realistic and ensuring your\ncode can handle different scenarios effectively. I recommend you take a look at\n[jio-test](#jio-test) and [property-based-testing](#pbt).\n\nUsing these stubs, the following events were printed out:\n\n```text\nStarted JFR stream for 2,000 sg in SignupTests\n\n------ eval-exp --------\n|  Expression: JsObjExpPar[timestamp]\n|  Result: SUCCESS\n|  Duration: 293,209 µs\n|  Output: 2024-02-13T09:18:21.071Z\n|  Context: signup\n|  Thread: main\n|  Event Start Time: 2024-02-13T10:18:21.071499125+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: count_number_users\n|  Result: SUCCESS\n|  Duration: 65,372 ms\n|  Output: 32634\n|  Context: signup\n|  Thread: virtual--1\n|  Event Start Time: 2024-02-13T10:18:21.066073417+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: JsObjExpPar[number_users]\n|  Result: SUCCESS\n|  Duration: 66,663 ms\n|  Output: 32634\n|  Context: signup\n|  Thread: virtual--1\n|  Event Start Time: 2024-02-13T10:18:21.065248375+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: JsObjExpPar[addresses]\n|  Result: SUCCESS\n|  Duration: 116,944 ms\n|  Output: [\"m\",\"n\",\"g\"]\n|  Context: signup\n|  Thread: virtual--1\n|  Event Start Time: 2024-02-13T10:18:21.071124667+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: IfElseExp-predicate\n|  Result: SUCCESS\n|  Duration: 37,233 ms\n|  Output: true\n|  Context: signup\n|  Thread: virtual--1\n|  Event Start Time: 2024-02-13T10:18:21.218791959+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: IfElseExp-consequence\n|  Result: SUCCESS\n|  Duration: 9,667 µs\n|  Output: QREuMrvmtunCvhbZxykT\n|  Context: signup\n|  Thread: virtual--1\n|  Event Start Time: 2024-02-13T10:18:21.256122125+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: IfElseExp\n|  Result: SUCCESS\n|  Duration: 37,386 ms\n|  Output: QREuMrvmtunCvhbZxykT\n|  Context: signup\n|  Thread: virtual--1\n|  Event Start Time: 2024-02-13T10:18:21.218788042+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: JsObjExpPar[id]\n|  Result: SUCCESS\n|  Duration: 184,728 ms\n|  Output: QREuMrvmtunCvhbZxykT\n|  Context: signup\n|  Thread: virtual--1\n|  Event Start Time: 2024-02-13T10:18:21.071473417+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: JsObjExpPar\n|  Result: SUCCESS\n|  Duration: 191,692 ms\n|  Output: {\"addresses\":[\"m\",\"n\",\"g\"],\"number_users\":32634,\"timestamp\":\"2024-02-13T09:18:21.071Z\",\"id\":\"QREuMrvmtunCvhbZxykT\"}\n|  Context: signup\n|  Thread: virtual--1\n|  Event Start Time: 2024-02-13T10:18:21.064957792+01:00\n-------------------------\n\n```\n\nTo enhance the resilience of our code, let's introduce some retry logic for the `countUsers`\nsupplier. We want to allow up to three retries:\n\n```code\n        // let's add up to three retries\n        countUsers.debug(EventBuilder.of(\"count_users\", context))\n                  .retry(RetryPolicies.limitRetries(3))\n                  .recover(_ -\u003e -1)\n```\n\nIn this code:\n\n- The `countUsers` supplier is executed, and for each execution, the `debug` method creates an\n  event. The `EventBuilder` allows you to specify the name of the expression being evaluated\n  (\"count_users\") and the context. This helps customize the events sent to the JFR system.\n\n- The `retry` method is used to introduce retry logic. In case of failure, `countUser` will be\n  retried up to three times.\n\n- The `recover` method specifies what value to return in case of a failure.\n\nAnd to test it, let's change the stub for the `countUser` supplier:\n\n```java\n\n//let's change the delay of every stub to 1 sec, for the sake of clarity\nGen\u003cDuration\u003e delayGen = Gen.cons(1)\n                            .map(Duration::ofSeconds);\n\nIO\u003cInteger\u003e countUsers =\n    StubBuilder.ofGen(Gen.seq(n -\u003e n \u003c= 4 ?\n                                   IO.fail(new RuntimeException(n + \"\")) :\n                                   IO.succeed(n)\n                             )\n                     )\n               .withDelays(delayGen)\n               .withExecutor(Executors.newVirtualThreadPerTaskExecutor())\n               .get();\n\n```\n\nIn this code:\n\n- The generator `delayGen` provides a constant delay of 1 second.\n\n- The `countUsers` effect is defined to use the `StubBuilder` with a sequence generator (`Gen.seq`)\n  that allows you to choose different values for each call. In this case, the first four calls\n  trigger a failure, which is treated as a value that can be returned.\n\nThis setup allows you to test and observe the retry logic in action:\n\n```text\nStarted JFR stream for 10,000 sg in SignupTests\n\n------ eval-exp --------\n|  Expression: JsObjExpPar[timestamp]\n|  Result: SUCCESS\n|  Duration: 281,583 µs\n|  Output: 2024-02-13T09:30:42.681Z\n|  Context: signup\n|  Thread: main\n|  Event Start Time: 2024-02-13T10:30:42.681326792+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: count_number_users\n|  Result: FAILURE\n|  Duration: 1,010 sg\n|  Output: java.lang.RuntimeException: 1\n|  Context: signup\n|  Thread: virtual--1\n|  Event Start Time: 2024-02-13T10:30:42.678849959+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: JsObjExpPar[addresses]\n|  Result: SUCCESS\n|  Duration: 1,007 sg\n|  Output: [\"l\",\"e\",\"B\"]\n|  Context: signup\n|  Thread: virtual--1\n|  Event Start Time: 2024-02-13T10:30:42.681127792+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: IfElseExp-predicate\n|  Result: SUCCESS\n|  Duration: 1,006 sg\n|  Output: false\n|  Context: signup\n|  Thread: virtual--1\n|  Event Start Time: 2024-02-13T10:30:43.6904075+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: count_number_users\n|  Result: FAILURE\n|  Duration: 1,006 sg\n|  Output: java.lang.RuntimeException: 2\n|  Context: signup\n|  Thread: virtual--1\n|  Event Start Time: 2024-02-13T10:30:43.690528334+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: count_number_users\n|  Result: FAILURE\n|  Duration: 1,004 sg\n|  Output: java.lang.RuntimeException: 3\n|  Context: signup\n|  Thread: not recorded\n|  Event Start Time: 2024-02-13T10:30:44.696579667+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: PairExpSeq[1]\n|  Result: SUCCESS\n|  Duration: 1,001 sg\n|  Output: null\n|  Context: signup\n|  Thread: not recorded\n|  Event Start Time: 2024-02-13T10:30:44.702844292+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: PairExpSeq[2]\n|  Result: SUCCESS\n|  Duration: 1,003 sg\n|  Output: null\n|  Context: signup\n|  Thread: virtual--1\n|  Event Start Time: 2024-02-13T10:30:45.704042667+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: count_number_users\n|  Result: FAILURE\n|  Duration: 1,006 sg\n|  Output: java.lang.RuntimeException: 4\n|  Context: signup\n|  Thread: virtual--1\n|  Event Start Time: 2024-02-13T10:30:45.700588667+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: PairExpSeq\n|  Result: SUCCESS\n|  Duration: 2,004 sg\n|  Output: (null, null)\n|  Context: signup\n|  Thread: virtual--1\n|  Event Start Time: 2024-02-13T10:30:44.702836584+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: JsObjExpPar[number_users]\n|  Result: SUCCESS\n|  Duration: 4,030 sg\n|  Output: -1\n|  Context: signup\n|  Thread: virtual--1\n|  Event Start Time: 2024-02-13T10:30:42.67800425+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: IfElseExp-alternative\n|  Result: SUCCESS\n|  Duration: 2,015 sg\n|  Output: adnhvqDPCgmEINgqiteV\n|  Context: signup\n|  Thread: virtual--1\n|  Event Start Time: 2024-02-13T10:30:44.702804125+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: IfElseExp\n|  Result: SUCCESS\n|  Duration: 3,028 sg\n|  Output: adnhvqDPCgmEINgqiteV\n|  Context: signup\n|  Thread: virtual--1\n|  Event Start Time: 2024-02-13T10:30:43.690404584+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: JsObjExpPar[id]\n|  Result: SUCCESS\n|  Duration: 4,037 sg\n|  Output: adnhvqDPCgmEINgqiteV\n|  Context: signup\n|  Thread: virtual--1\n|  Event Start Time: 2024-02-13T10:30:42.681302584+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: JsObjExpPar\n|  Result: SUCCESS\n|  Duration: 4,042 sg\n|  Output: {\"addresses\":[\"l\",\"e\",\"B\"],\"number_users\":-1,\"timestamp\":\"2024-02-13T09:30:42.681Z\",\"id\":\"adnhvqDPCgmEINgqiteV\"}\n|  Context: signup\n|  Thread: virtual--1\n|  Event Start Time: 2024-02-13T10:30:42.676874584+01:00\n-------------------------\n\n```\n\nKey points:\n\n1. After the first failure and three retries, the value -1 from the `recover` function is returned\n\n2. The `retry` method can accept a predicate, allowing you to specify which errors should trigger a\n   retry. This fine-grained control is valuable for handling specific error scenarios.\n\n3. Retry policies in JIO are composable, making it easy to build complex retry strategies. For\n   example, you can create a policy like this:\n\n   ```code\n   RetryPolicies.constantDelay(Duration.ofMillis(50))\n                .limitRetriesByCumulativeDelay(Duration.ofMillis(300))\n   ```\n\n   This policy specifies a constant delay of 50 milliseconds between retries and limits retries by a\n   cumulative delay of 300 milliseconds.\n\n4. JIO excels at scalability. Even when dealing with complex logic, it maintains simplicity in the\n   expressions you write, avoiding the complexities of callback hell or other frameworks.\n\n5. JIO offers a high signal-to-noise ratio. It reduces verbosity, allowing you to express complex\n   operations succinctly and clearly.\n\n## \u003ca name=\"Introduction\"\u003e\u003ca/\u003e Introduction\n\nFunctional Programming is all about working with pure functions and values. That's all. **However,\nwhere FP especially shines is dealing with effects**.\n\nBut what is an effect?\n\nFirst take a look at the following piece of code:\n\n```code\n\nint a = sum(1,2) + 3;\n\nint b = sum(1,2) + 1;\n\n```\n\nAs far as the function `sum` is **pure**, you can refactor the previous piece of code and call the\nfunction just once:\n\n```code\n\nint c = sum(1,2);\n\nint a = c + 3;\n\nint b = c + 1;\n\n```\n\nBoth programs are equivalents and wherever you see `sum(1,2)` you can replace it by `c` without\nchanging the meaning of the program at all.\n\nAn effect, on the other hand, is something you can't call more than once unless you intended to:\n\n```code\n\nInstant a = Instant.now().plus(Period.ofDays(1));\n\nInstant b = Instant.now().plus(Period.ofDays(2));\n\n```\n\nBecause _now()_ returns a different value each time it's called and therefore is not a pure\nfunction, the following refactoring would change completely the meaning of the program (and still\nyour favourite IDE suggests you to do it at times!):\n\n```code\n\nInstant now = Instant.now();\n\nInstant a = now.plus(Period.ofDays(1));\n\nInstant b = now.plus(Period.ofDays(2));\n\n```\n\nHere's when laziness comes into play. Since Java 8, we have suppliers. They are indispensable to do\nFP in Java. The following piece of code is equivalent to the original one without changing the\nmeaning of the program:\n\n```code\n\nSupplier\u003cInstant\u003e now = () -\u003e Instant.now();\n\nInstant a = now.get().plus(Period.ofDays(1));\n\nInstant b = now.get().plus(Period.ofDays(2));\n\n```\n\nThis property that allows you to factor out expressions is called **referential transparency**, and\nit's fundamental to create and compose expressions.\n\nWhat can you expect from JIO:\n\n- Simple and powerful API\n- Errors are first-class citizens\n- Simple and powerful testing tools ([jio-test](#jio-test))\n- Easy to extend and get benefit from all the above. Examples are [jio-http](#jio-http),\n  [jio-mongodb](#jio-mongodb) and [jio-jdbc](#jio-jdbc). And you can create your own integrations!\n- I don't fall into the logging-library war. This is something that sucks in Java. I just use Java\n  Flight Recording!\n- Almost zero dependencies (just plain Java!)\n- JIO doesn't transliterate any functional API from other languages. This way, any standard Java\n  programmer will find JIO quite easy and familiar.\n\n---\n\n## \u003ca name=\"jio-exp\"\u003e\u003ca/\u003e jio-exp\n\n[![Maven](https://img.shields.io/maven-central/v/com.github.imrafaelmerino/jio-exp/3.0.0-RC2)](https://search.maven.org/artifact/com.github.imrafaelmerino/jio-exp/3.0.0-RC2/jar\n\"jio-ex\")\n\nLet's model a functional effect in Java!\n\n```code\n\nimport java.util.function.Supplier;\nimport java.util.concurrent.CompletableFuture;\n\npublic sealed abstract class IO\u003cOutput\u003e implements Callable\u003cResult\u003cOutput\u003e\u003e permits Exp, Val {\n\n    @Override\n    Result\u003cOutput\u003e call() throws Exception;\n\n    Result\u003cO\u003e compute();\n\n    //other methods\n\n}\n\npublic sealed interface Result\u003cOutput\u003e permits Result.Success, Result.Failure {\n\n   //methods\n\n}\n\n```\n\nKey Concepts:\n\n- **`IO` Definition**: The `IO` class is a fundamental component of JIO. It's an abstract class\n  designed to represent functional effects or computations.\n\n- **Lazy Computation**: `IO` is a lazy computation and is realized as a `Callable`. In essence, it\n  merely outlines a computation without immediate execution, awaiting the explicit invocation of\n  methods like `call()` or `compute()`. It's important to note that both operations are blocking,\n  which isn't an issue when employing virtual threads.\n\n- **Handling Errors**: A critical aspect of JIO is that `Result` can represent both successful and\n  failed computations. This approach ensures that errors are treated as first-class citizens,\n  avoiding the need to throw exceptions whenever an error occurs.\n\n- According to Erik Meyer, as mentioned in [this\n  video](https://www.youtube.com/watch?v=z0N1aZ6SnBk), honesty is at the core of functional\n  programming. I find this perspective to be quite insightful. Latency and failures hold such a\n  significance that they should be explicitly denoted in a function or method's signature with the\n  `IO` type. Without this distinction, it becomes impossible to differentiate functions that are\n  free from failure and latency from those that aren't, making our code difficult to reason about.\n\n- The `call` and `compute` methods exhibit significant similarity. The `call` method is essential\n  due to IO's implementation of `Callable`, facilitating seamless integration with the structural\n  concurrency API:\n\n```code\nIO first = ???;\nIO second = ???;\n\ntry (var scope = new StructuredTaskScope.ShutdownOnFailure()) {\n\n    // Since IO is a callable, we can pass it in the `fork` method\n    Subtask\u003cResult\u003cFirst\u003e\u003e first = scope.fork(first);\n    Subtask\u003cResult\u003cSecond\u003e\u003e second = scope.fork(second);\n    ....\n}\n\n```\n\nIn most cases, apart from the one described above, it is advisable to use the `compute` method. This\nmethod does not throw a checked exception and returns a `Result` object encapsulating the outcome of\nthe computation (either success or failure).\"\n\n- According to its `permits` definition, IO has two distinct subclasses: `Val` and `Exp`\n\n- **`Val`**: This subclass denotes an effect that is computed and returned as a result.\n\n- **`Exp`**: This subclass signifies an expression composed of multiple effects, which will be\n  computed and combined into the final result through an expression. Some examples of expressions\n  that we will see later are `PairExp`,`JsObjExp`,`CondExp`, `ListExp`, `SwitchExp` etc.\n\n---\n\n### \u003ca name=\"creating-effects\"\u003e\u003ca/\u003e Creating effects\n\nNow that we got the ball rolling, let's learn how to create IO effects.\n\n**From a constant or a computed value**\n\n```code\n\nIO\u003cString\u003e effect = IO.succeed(\"hi\");\n\nJsObj get(int id) { ??? }\nIO\u003cString\u003e effect = IO.succeed(get(1)); //get(1) is invoked before constructing the effect\n\n```\n\nIn both of the above examples, the effect will always compute the same value: either \"hi\" or the\nresult of calling `get(1)`. There is no lazynes here, a value is computed right away and used to\ncreate the IO effect\n\n**From an exception**\n\n```code\n\nIO\u003cString\u003e effect = IO.fail(new RuntimeException(\"something went wrong :(\"));\n\n```\n\nLike with `succeed`, the effect will always produce the same result, in this case it fails always\nwith the same exception, which is instantiated before creating the effect. Do notice that no\nexception is thrown!\n\n**From a lazy computation or a supplier**\n\nThis is a very common case, and you will use it all the time to create effects.\n\n```code\n\nSuplier\u003cJsObj\u003e computation = ???;\nIO\u003cLong\u003e effect = IO.lazy(computation);\n\n```\n\nIn this example and effect is created but not like in `succeed` and `fail`, **nothing is evaluated**\nsince a `Supplier` is lazy. It's very important to notice the difference. On the other hand, each\ntime the `get` or `result` methods are invoked a potentially new value can be returned.\n\n**From a callable**\n\nWe can think of a `Callable` as lazy computations like `Suppliers`, but with the important\ndifference that they can fail.\n\n```code\n\nCallable\u003cLong\u003e callable = ???;\n\nIO\u003cLong\u003e effect = IO.task(callable);\n\n```\n\n**From a Future**:\n\n```code\n\nFuture\u003cJsObj\u003e get(String id){ ??? }\n\nIO\u003cJsObj\u003e effect = IO.effect( () -\u003e get(1) );\n\n```\n\nLike with `lazy` and `task`, the previous example doesn't evaluate anything to create the effect  \nsince the effect method takes in a `Supplier`.\n\n**From auto-closable resources**\n\nThe `resource` method is used to create an IO effect that manages a resource implementing the\n`AutoCloseable` interface. It takes a `Callable` that supplies the closable resource and a mapping\nfunction to transform the resource into a value. This method ensures proper resource management,\nincluding automatic closing of the resource after the map function is executed, to prevent memory\nleaks. It returns an IO effect encapsulating both the resource handling and mapping.\n\n```code\n\nstatic \u003cO, I extends AutoCloseable\u003e IO\u003cO\u003e resource(Callable\u003cI\u003e resource,\n                                                   Lambda\u003cI, O\u003e map\n                                                   );\n```\n\nand an example:\n\n```code\n\nCallable\u003cFileInputStream\u003e callable = () -\u003e new FileInputStream(\"example.txt\");\n\n// Create an IO effect using the resource method\nIO\u003cString\u003e resultEffect =\n         IO.resource(callable,\n                     inputStream -\u003e {\n                                     try {\n                                        // Read the content of the file and return it as a String\n                                          byte[] bytes = new byte[inputStream.available()];\n                                          inputStream.read(bytes);\n                                          return IO.succeed(new String(bytes,\n                                                            StandardCharsets.UTF_8));\n                                          }\n                                          catch (IOException e) {\n                                             return IO.fail(e);\n                                          }\n                                     }\n                     );\n```\n\n**Other regular IO effects**\n\n```code\n\nIO\u003cBoolean\u003e t = IO.TRUE\n\nIO\u003cBoolean\u003e f = IO.FALSE;\n\nIO\u003cString\u003e s = IO.NULL();\nIO\u003cInteger\u003e s = IO.NULL();\n\n```\n\nThe `NULL` method creates an IO effect that always produces a result of null. It is a generic method\nthat captures the type of the caller, allowing you to create null effects with different result\ntypes. This can be useful when you need to type of the caller, allowing you to create null effects\nwith different result types. This can be useful when you need to represent a null result in your\nfunctional code. These constants, `TRUE` and `FALSE`, represent IO effects that always succeed with\n`true` and `false`, respectively.\n\n---\n\n### \u003ca name=\"lambdas\"\u003e\u003ca/\u003e Lambdas\n\nIn the world of JIO, working with effectful functions is a common practice. The following functions\nreturn `IO` effects, and you'll often encounter them in your code:\n\n```code\n\nFunction\u003cI, IO\u003cO\u003e\u003e\n\nBiFunction\u003cA,B, IO\u003cO\u003e\u003e\n\n```\n\nTo make our code more concise and readable, we can give these effectful functions an alias. Let's\ncall them \"Lambdas\":\n\n```code\n\ninterface Lambda\u003cI, O\u003e extends Function\u003cI, IO\u003cO\u003e\u003e {}\n\ninterface BiLambda\u003cA, B, O\u003e extends BiFunction\u003cA, B, IO\u003cO\u003e\u003e {}\n\n```\n\nLambdas are similar to regular functions, but there's one key difference: they never throw\nexceptions. In JIO, exceptions are treated as first-class citizens, just like regular values.\n\nConverting regular functions or predicates into Lambdas is straightforward using the lift methods:\n\n```\n\nFunction\u003cInteger, Integer\u003e opposite = n -\u003e -n;\nBiFunction\u003cInteger, Integer, Integer\u003e sum = (a,b) -\u003e a + b;\nPredicate\u003cInteger\u003e isOdd = n -\u003e n % 2 == 1;\n\nLambda\u003cInteger, Integer\u003e l1 = Lambda.liftFunction(opposite);\nLambda\u003cBoolean, Integer\u003e l2 = Lambda.liftPredicate(isOdd);\nBiLambda\u003cInteger, Integer, Integer\u003e l3 = BiLambda.liftFunction(sum);\n\n```\n\nThe `then` method is a powerful feature of the `Lambda` interface in JIO that allows you to compose\nand sequence effects in a functional and expressive manner. When you have two `Lambda` instances,\nyou can use the `then` method to create a new `Lambda` that combines the effects of the two original\nlambdas. When you apply the composed `Lambda` to an input, it executes the first `Lambda`, followed\nby the second `Lambda`, creating a sequence of effects. This composition is especially useful when\nbuilding complex workflows or pipelines of operations. It enhances the readability and\nexpressiveness of your code by chaining together effects in a natural and intuitive way.\n\n```code\n\nLambda\u003cA,B\u003e first = ???;\nLambda\u003cB,C\u003e second = ???\n\nLambda\u003cA,C\u003e third = first.then(second);\n\n\n```\n\n---\n\n### \u003ca name=\"operations-with-effects\"\u003e\u003ca/\u003e Operations with effects\n\n#### Making our code more resilient being persistent!\n\nRetrying failed operations is a crucial aspect of handling errors effectively in JIO. In real-world\nscenarios, errors can sometimes be transient or caused by temporary issues, such as network glitches\nor resource unavailability.  \nBy incorporating retry mechanisms, JIO empowers you to gracefully recover from such errors without\ncompromising the stability of your application. Whether it's a network request, database query, or\nany other effect, JIO's built-in retry functionality allows you to define retry policies, such as\nexponential backoff or custom strategies, to ensure that your operations have a higher chance of\nsucceeding. This approach not only enhances the robustness of your application but also minimizes\nthe impact of transient errors, making JIO a valuable tool for building resilient and reliable\nsystems.\n\n```code\n\npublic sealed abstract class IO\u003cOutput\u003e implements Callable\u003cResult\u003cOutput\u003e\u003e permits Exp, Val {\n\n  IO\u003cO\u003e retry(Predicate\u003cThrowable\u003e predicate,\n              RetryPolicy policy\n             );\n\n  IO\u003cO\u003e repeat(Predicate\u003cOutput\u003e predicate,\n               RetryPolicy policy\n              );\n}\n\n```\n\nWhile the `retry` method is primarily used to retry an operation when an error occurs (based on a\nspecified exception condition), the `repeat` method allows you to repeat an operation based on the\nresult or outcome of the effect itself,  \nproviding flexibility for scenarios where retries are needed for reasons other than errors. Retry\npolicies are created in a very declarative and composable way, for example:\n\n```code\n\nDuration oneHundredMillis = Duration.ofMillis(100);\n\nDuration oneSec = Duration.ofSeconds(1);\n\n// up to five retries waiting 100 ms\nRetryPolicies.constantDelay(oneHundredMillis)\n             .append(limitRetries(5))\n\n// during 3 seconds up to 10 times\nRetryPolicies.limitRetries(10)\n             .limitRetriesByCumulativeDelay(Duration.ofSeconds(3))\n\n// 5 times without delay and then, if it keeps failing,\n// an incremental delay from 100 ms up to 1 second\nRetryPolicies.limiteRetries(5)\n             .followedBy(incrementalDelay(oneHundredMillis)\n             .capDelay(oneSec))\n\n```\n\nThere are very interesting policies implemented based on [this\narticle](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/): exponential\nbackoff, full jitter, equal jitter, decorrelated jitter etc\n\n#### Making our code more resilient having a Backup plan!\n\nIn scenarios where errors persist despite retries, JIO offers robust error-handling mechanisms to\nensure your application maintains resilience. Three key methods come into play:\n\n```code\n\npublic sealed abstract class IO\u003cOutput\u003e implements Callable\u003cResult\u003cOutput\u003e\u003e permits Exp, Val {\n\n  IO\u003cOutput\u003e recover(Function\u003cThrowable, Output\u003e fn);\n\n  IO\u003cOutput\u003e recoverWith(Lambda\u003cThrowable, Output\u003e fn);\n\n  IO\u003cOutput\u003e fallbackTo(Lambda\u003cThrowable, Output\u003e fn);\n\n}\n\n```\n\n**recover**: This method allows you to gracefully recover from an error by providing a function that\nmaps the encountered exception to a fallback value of type 'O.' It ensures that your application can\ncontinue its operation even in the face of unexpected errors.\n\n**recoverWith**: This method allows you to gracefully recover from an error by providing a function\nthat maps the encountered exception to a fallback value of type 'O.' It ensures that your\napplication can continue its operation even in the face of unexpected errors.\n\n**fallbackTo**: Similar to 'recoverWith,' 'fallbackTo' allows you to switch to an alternative effect\n(specified by the provided function) when an error occurs. However, it introduces an important\ndistinction: if the alternative effect also encounters an error, 'fallbackTo' will return the\noriginal error from the first effect. This ensures that error propagation is maintained while\nenabling you to gracefully handle errors and fallback to alternative operations when needed.\n\n#### Being Functional!\n\nJIO encourages a functional programming style with the following methods:\n\n```code\n\npublic sealed abstract class IO\u003cOutput\u003e implements Callable\u003cResult\u003cOutput\u003e\u003e permits Exp, Val {\n\n    IO\u003cMappedOutput\u003e map(Function\u003cOutput, MappedOutput\u003e fn);\n\n    IO\u003cOutput\u003e mapFailure(Function\u003cThrowable, Throwable\u003e);\n\n    IO\u003cMappedOutput\u003e then(Lambda\u003cOutput, MappedOutput\u003e fn);\n\n}\n\n```\n\n- `map`: Transforms the successful result of an effect using a provided function, allowing you to\n  map values from one type to another.\n- `mapFailure`: Transforms the failure result of an effect using a provided function, allowing you\n  to map exceptions from one type to another.\n- `then` (akin to `flatMap` or `bind` in other languages and a core function in monads): Applies a\n  lambda function to  \n  the result of the effect, creating a new effect that depends on the previous result. The name\n  'then' is used here for conciseness.\n\n#### Houston, we have a problem!\n\n```code\n\npublic sealed abstract class IO\u003cOutput\u003e implements Callable\u003cResult\u003cOutput\u003e\u003e permits Exp, Val {\n\n    IO\u003cOutput\u003e debug();\n\n    IO\u003cOutput\u003e debug(EventBuilder builder);\n}\n\n\n```\n\nDebugging and logging events play a pivotal role in software development. Some of the advantages\nthat you'll get are:\n\n1. **Simplifying Debugging**: Debugging is the process of identifying and fixing issues in your\n   code. When testing an application, developers often need to trace the execution of specific parts\n   of their code to locate bugs or performance bottlenecks. These debugging methods allow you to\n   attach debug mechanisms to expressions, making it easier to monitor and log the execution of each\n   operand individually. This granular level of insight is invaluable when diagnosing and resolving\n   problems.\n\n2. **Testing Efficiency**: During the testing phase, the ability to debug and log events efficiently\n   is a time-saving and productivity-enhancing feature. These methods let you generate and send\n   events to the Flight Recorder system, which can then be analyzed to gain insights into the\n   behavior of your code. You can customize these events using the provided `EventBuilder`,\n   tailoring the information collected to suit your specific testing needs.\n\n3. **No Setup Overhead**: One of the key advantages of these methods is that they require minimal\n   setup. This means that you can integrate debugging and event logging into your codebase without\n   the need for extensive configuration or external tools. It's a straightforward and hassle-free\n   way to gather essential information about the execution of your code.\n\n4. **Recursive Debugging with debugEach**: When you apply the debugEach method to an expression, it\n   attaches a debugging mechanism to that expression and all its subexpressions. If any\n   subexpression within the main expression is itself an expression, debugEach is applied to it\n   recursively, passing along the same context.\n\n5. **Customization with EventBuilder**\n\nCustomization with the `EventBuilder` is a powerful feature that streamlines your debugging and\nevent logging. It allows you to tailor events to your specific needs and focus on the most relevant\ninformation:\n\n- **Event Naming**: You can categorize events by specifying the name for the expression being\n  evaluated and the context you're observing, making it easier to analyze different aspects of your\n  code.\n\n- **Mapping Success Output**: Transform the result of a successful expression into a format that\n  provides clear and concise information, helping you capture expected outcomes.\n\n- **Mapping Failure Output**: Customize how exceptions are presented by mapping `Throwable` objects\n  into a format that aids in debugging and troubleshooting. This can include error messages, stack\n  traces, or other relevant details.\n\nIn conclusion, the ability to debug and log events with minimal setup is highly valuable for\ndevelopers. It simplifies the debugging process, enhances testing efficiency, and provides data-rich\ninsights into code execution. These methods are indispensable tools for maintaining software\nquality, diagnosing issues, and optimizing performance during the development and testing phases.\n\n#### Being Impatient!\n\n```code\n\npublic sealed abstract class IO\u003cOutput\u003e implements Callable\u003cResult\u003cOutput\u003e\u003e permits Exp, Val {\n\n    IO\u003cVoid\u003e async();\n}\n```\n\n- `async`: Allows executing an action without waiting for its result and returns immediately. It is\n  useful when you are not interested in the outcome of the effect (returns void) and want to trigger\n  it asynchronously.\n\n#### Being sneaky!\n\nSometimes, you need to sneak a peek into the execution of an effect:\n\n```code\npublic sealed abstract class IO\u003cOutput\u003e implements Callable\u003cResult\u003cOutput\u003e\u003e permits Exp, Val {\n\n    IO\u003cOutput\u003e peekFailure(Consumer\u003cThrowable\u003e failConsumer);\n\n    IO\u003cOutput\u003e peekSuccess(Consumer\u003cOutput\u003e successConsumer);\n\n    IO\u003cO\u003e peek(Consumer\u003cOutput\u003e successConsumer,\n               Consumer\u003cThrowable\u003e failureConsumer\n              );\n}\n\n```\n\n- `peekFailure`: Allows you to observe and potentially handle failures by providing a consumer that\n  logs exceptions in the JFR (Java Flight Recorder) system.\n- `peekSuccess`: Similarly, you can observe and process successful results using a success consumer.\n- `peek`: Combines both success and failure consumers, giving you full visibility into the effect's\n  execution.  \n  Exceptions occurring here are logged in the JFR system and do not alter the result of the effect.\n\n### I race you!\n\nWhen you require a result as quickly as possible among multiple alternatives, and you're uncertain\nwhich one will be the fastest:\n\n```code\n\npublic sealed abstract class IO\u003cOutput\u003e implements Callable\u003cResult\u003cOutput\u003e\u003e permits Exp, Val {\n\n    static \u003cOutput\u003e IO\u003cOutput\u003e race(IO\u003cOutput\u003e first, IO\u003cOutput\u003e... others);\n\n}\n\n```\n\n`race` method returns the result of the first effect that completes successfully, allowing you to\nmake quick decisions based on the outcome. \"It employs foundational structural concurrency, which is\na preview functionality introduced in Java 21. Specifically, it leverages the\n`StructuredTaskScope.ShutdownOnSuccess` scope.\n\n#### Pulling the trigger!\n\n\"To initiate the computation for deriving the final result, one must block and await the completion\nby invoking the `compute()` method. Notably, blocking is not a concern in this context as it is\nexpected to be executed from a virtual thread. This operation does not throw exceptions directly;\ninstead, it encapsulates potential outcomes within the `Result` type:\n\n```code\n\n   //nothing is evaluated here\n   IO\u003cInteger\u003e countUsers = ???;\n\n   //evaluation is triggered\n   Result\u003cInteger\u003e countUsersResult = countUsers.compute();\n\n   //we take advantage of patter matching to process the result for\n   //both possible outcomes: success or failure\n\n   String message =\n       switch (countUsersResult) {\n         case Success\u003cInteger\u003e success -\u003e {\n           switch (success.output()) {\n             case 0 -\u003e {\n               yield \"no users!\";\n             }\n\n             case Integer users when users \u003e 0 -\u003e {\n               yield \"number of users is greater than zero!\";\n             }\n\n             case Integer _ -\u003e {\n               yield \"number of users is lower than zero\";\n             }\n           }\n         }\n         case Failure\u003cInteger\u003e failure -\u003e {\n           switch (failure.exception()) {\n             case ConnectException _ -\u003e {\n               yield \"Try again!\";\n             }\n             case SocketTimeoutException _  -\u003e {\n               yield \"so impatient!\";\n             }\n             case Exception _ -\u003e {\n               yield \"maybe the next time!\";\n             }\n           }\n         }\n       };\n\n```\n\nWhen dealing with a `Result`, pattern matching is preferred whenever feasible. However, you can\nstill utilize the `Result` API to retrieve the final output:\n\n```code\nResult\u003cInteger\u003e countUsersResult;\n\n//throws an unchecked exception in case of failure (wraps the real failure in a `CompletionException`)\nInteger users = effect.getOutput();\n\n//throws a checked exception (the real failure) in case of failure\nInteger users = effect.getOutputOrThrow();\n\n```\n\n`getOutput` is more suitable when the API being used does not align well with checked exceptions,\nsuch as with `Stream` or `Function`. Conversely, `getOutputOrThrow` mandates handling the potential\nexception and ensures that the thrown exception is the real failure and not a CompletionException\n\n---\n\n### \u003ca name=\"expressions\"\u003e\u003ca/\u003e Expressions\n\n**Using expressions and function composition is how we deal with complexity in Functional\nProgramming**.  \nWith the following expressions, you will have a comprehensive toolkit to model effects, combine them\nin powerful ways, and tame complexity effectively.\n\n### IfElseExp\n\nThe `IfElseExp` expression allows you to conditionally choose between two computations based on a\npredicate. If the  \npredicate evaluates to true, the consequence is computed, and if it's false, the alternative\ncomputation is chosen. Both the consequence and alternative are represented as lazy computations of\nIO effects.\n\n```code\n\nIO\u003cO\u003e exp = IfElseExp.\u003cO\u003epredicate(IO\u003cBoolean\u003e condition)\n                     .consequence(Supplier\u003cIO\u003cO\u003e\u003e consequence)\n                     .alternative(Supplier\u003cIO\u003cO\u003e\u003e alternative);\n\n\n```\n\nIn this code, the `consequence` and `alternative` parameters are represented as `Supplier`\ninstances, which means they are not computed during the construction of the expression but rather\nevaluated only when needed, based on the result of the condition. This deferred execution allows for\nefficient conditional evaluation of IO effects.\n\n### SwitchExp\n\nYour original sentence is mostly correct but could benefit from a minor improvement for clarity:\n\nThe `SwitchExp` expression simulates the behavior of a switch construct, enabling multiple\npattern-value branches. It evaluates an effect or a value of type `I` and facilitates the use of\nmultiple clauses based on the evaluation. The `match` method compares the value or effect with\npatterns and selects the corresponding lambda (which takes in the value of type `I`). Patterns can\nencompass values, lists of values, or even predicates. It's possible to specify a default branch, in\ncase no pattern matches the input (otherwise the expression is reduced to `IO.NULL`).\n\n```code\n\n// matches a value of type I\n\nIO\u003cO\u003e exp =\n          SwitchExp\u003cI,O\u003e.eval(I value)\n                        .match(I pattern1, Lambda\u003cI,O\u003e lambda1,\n                               I pattern2, Lambda\u003cI,O\u003e lambda2,\n                               I pattern3, Lambda\u003cI,O\u003e lambda3,\n                               Lambda\u003cI,O\u003e otherwise          //optional\n                              );\n\n// matches an effect of type I\n\nIO\u003cO\u003e exp=\n        SwitchExp\u003cI, O\u003e.eval(IO\u003cI\u003e effect)\n                       .match(I pattern1, Lambda\u003cI,O\u003e lambda1,\n                              I pattern2, Lambda\u003cI,O\u003e lambda2,\n                              I pattern3, Lambda\u003cI,O\u003e lambda3,\n                              Lambda\u003cI,O\u003e otherwise         //optional\n                             );\n\n\n// For example, the following expression reduces to \"3 is Wednesday\"\n\nIO\u003cO\u003e exp=\n         SwitchExp\u003cString\u003e.eval(3)\n                          .match(1, n -\u003e IO.succedd(n + \" is Monday\"),\n                                 2, n -\u003e IO.succedd(n + \" is Tuesday\"),\n                                 3, n -\u003e IO.succedd(n + \" is Wednesday\"),\n                                 4, n -\u003e IO.succedd(n + \" is Thursday\"),\n                                 5, n -\u003e IO.succedd(n + \" is Friday\"),\n                                 n -\u003e IO.succedd(n + \" is weekend\")\n                                );\n```\n\nThe same as before but using lists instead of constants as patterns.\n\n```code\n\nIO\u003cO\u003e exp =\n    SwitchExp\u003cI, O\u003e.eval(I value)\n                   .matchList(List\u003cI\u003e pattern1, Lambda\u003cI,O\u003e lambda1,\n                              List\u003cI\u003e pattern2, Lambda\u003cI,O\u003e lambda2,\n                              List\u003cI\u003e pattern3, Lambda\u003cI,O\u003e lambda3,\n                              Lamda\u003cI,O\u003e otherwise\n                             );\n\n// For example, the following expression reduces to \"20 falls into the third week\"\nIO\u003cO\u003e exp=\n    SwitchExp\u003cInteger, String\u003e.eval(20)\n                              ..matchList(List.of(1, 2, 3, 4, 5, 6, 7),\n                                          n -\u003e IO.succeed(n + \" falls into the first week\"),\n                                          List.of(8, 9, 10, 11, 12, 13, 14),\n                                          n -\u003e IO.succeed(n + \" falls into the second week\"),\n                                          List.of(15, 16, 17, 18, 19, 20),\n                                          n -\u003e IO.succeed(n + \" falls into the third week\"),\n                                          List.of(21, 12, 23, 24, 25, 26, 27),\n                                          n -\u003e IO.succeedd(n + \" falls into the forth week\"),\n                                          n -\u003e IO.succeed(n + \" falls into the last days of the month\")\n                                         );\n```\n\nLast but not least, you can use predicates as patterns instead of values or list of values:\n\n```code\n\nIO\u003cO\u003e exp=\n        SwitchExp\u003cI, O\u003e.eval(IO\u003cI\u003e value)\n                       .matchPredicate(Predicate\u003cI\u003e pattern1, Lambda\u003cI,O\u003e lambda1,\n                                       Predicate\u003cI\u003e pattern2, Lambda\u003cI,O\u003e lambda2,\n                                       Predicate\u003cI\u003e pattern3, Lambda\u003cI,O\u003e lambda3\n                                      );\n\n// For example, the following expression reduces to the default value:\n// \"20 is greater or equal to ten\"\n\nIO\u003cO\u003e exp=\n        SwitchExp\u003cInteger, String\u003e.eval(IO.succeed(20))\n                                  .matchPredicate(n -\u003e n \u003c 5,\n                                                  n -\u003e IO.succeed(n + \"is lower than five\"),\n                                                  n -\u003e n \u003c 10,\n                                                  n -\u003e IO.succeed(n + \"is lower than ten\"),\n                                                  n-\u003e n \u003e 10,\n                                                  n -\u003e IO.succeed(n + \"is greater or equal to ten\")\n                                                  );\n```\n\n### CondExp\n\n`CondExp` is a set of branches and a default effect. Each branch consists of an effect that computes\na boolean (the  \ncondition) and its associated effect. The expression is reduced to the value of the first branch\nwith a true condition, making the order of branches significant. If no condition is true, it\ncomputes the default effect if specified (otherwise the expression is reduced to `IO.NULL`)\n\n```code\n\nIO\u003cO\u003e exp=\n    CondExp.\u003cO\u003eseq(IO\u003cBoolean\u003e cond1, Supplier\u003cIO\u003cO\u003e\u003e effect1,\n                   IO\u003cBoolean\u003e cond2, Supplier\u003cIO\u003cO\u003e\u003e effect2,\n                   IO\u003cBoolean\u003e cond3, Supplier\u003cIO\u003cO\u003e\u003e effect3,\n                   Supplier\u003cIO\u003cO\u003e\u003e otherwise                   //optional\n                  );\n\n\nIO\u003cO\u003e exp =\n    CondExp.\u003cO\u003epar(IO\u003cBoolean\u003e cond1, Supplier\u003cIO\u003cO\u003e\u003e effect1,\n                   IO\u003cBoolean\u003e cond2, Supplier\u003cIO\u003cO\u003e\u003e effect2,\n                   IO\u003cBoolean\u003e cond3, Supplier\u003cIO\u003cO\u003e\u003e effect3,\n                   Supplier\u003cIO\u003cO\u003e\u003e otherwise                  //optional\n                  );\n\n```\n\n### AllExp and AnyExp\n\n`AllExp` and `AnyExp` provide idiomatic boolean expressions for \"AND\" and \"OR.\" They allow you to\ncompute multiple boolean effects, either sequentially or in parallel.\n\n```code\n\nIO\u003cBoolean\u003e allPar = AllExp.par(IO\u003cBoolean\u003e cond1, IO\u003cBoolean\u003e cond2,....);\nIO\u003cBoolean\u003e allSeq = AllExp.seq(IO\u003cBoolean\u003e cond1, IO\u003cBoolean\u003e cond2,....);\n\nIO\u003cBoolean\u003e anyPar = AnyExp.par(IO\u003cBoolean\u003e cond1, IO\u003cBoolean\u003e cond2,...);\nIO\u003cBoolean\u003e anySeq = AnyExp.seq(IO\u003cBoolean\u003e cond1, IO\u003cBoolean\u003e cond2,...);\n\n```\n\nYou can also create AllExp or AnyExp from streams of IO\u003cBoolean\u003e using the `parCollector` and\n`seqCollector`\n\n```code\n\nLambda\u003cVehicle, Boolean\u003e isFerrari = ???\n\nList\u003cVehicle\u003e vehicles = ???;\n\nAllExp allFerrariPar = vehicles.stream()\n                               .map(isFerrary)\n                               .collector(AllExp.parCollector());\n\nAllExp allFerrariSeq = vehicles.stream()\n                               .map(isFerrary)\n                               .collector(AllExp.seqCollector());\n\nAnyExp anyFerrariSeq = vehicles.stream()\n                               .map(isFerrary)\n                               .collector(AnyExp.seqCollector());\n\n\n```\n\n### PairExp and TripleExp\n\n`PairExp` and `TripleExp` allow you to zip effects into tuples of two and three elements,\nrespectively. You can compute each element either in parallel or sequentially.\n\n```code\n\nIO\u003cPair\u003cA, B\u003e pairPar = PairExp.par(IO\u003cA\u003e effect1,\n                                    IO\u003cB\u003e effect2);\n\nIO\u003cPair\u003cA, B\u003e pairSeq = PairExp.seq(IO\u003cA\u003e effect1,\n                                    IO\u003cB\u003e effect2);\n\nIO\u003cTriple\u003cA, B, C\u003e triplePar = TripleExp.par(IO\u003cA\u003e effect1,\n                                             IO\u003cB\u003e effect2,\n                                             IO\u003cC\u003e effect3);\n\nIO\u003cTriple\u003cA, B, C\u003e tripleSeq = TripleExp.seq(IO\u003cA\u003e effect1,\n                                             IO\u003cB\u003e effect2,\n                                             IO\u003cC\u003e effect3);\n\n```\n\n### JsObjExp and JsArrayExp\n\n`JsObjExp` and `JsArrayExp` are data structures resembling raw JSON. You can compute their values\nsequentially or in parallel. You can mix all the expressions discussed so far and nest them,\nproviding you with immense flexibility and  \npower in handling complex data structures.\n\n```code\n\nIfElseExp\u003cJsStr\u003e a = IfElseExp.\u003cJsStr\u003epredicate(IO\u003cBoolean\u003e cond1)\n                              .consequence(Supplier\u003cIO\u003cJsStr\u003e\u003e consequence)\n                              .alternative(Supplier\u003cIO\u003cJsStr\u003e\u003e alternative);\n\nJsArrayExp b =\n    JsArrayExp.seq(SwitchExp\u003cInteger, JsValue\u003e.match(n)\n                                              .patterns(n -\u003e n \u003c= 0, Supplier\u003cIO\u003cJsValue\u003e\u003e e1,\n                                                        n -\u003e n  \u003e 0, Supplier\u003cIO\u003cJsValue\u003e\u003e e2\n                                                       ),\n                   CondExp.par(IO\u003cBoolean\u003e cond2, Supplier\u003cIO\u003cJsValue\u003e\u003e e3,\n                               IO\u003cBoolean\u003e cond3, Supplier\u003cIO\u003cJsValue\u003e\u003e e4,\n                               Supplier\u003cIO\u003cJsValue\u003e\u003e otherwise\n                              )\n                 );\n\nJsObjExp c = JsObjExp.seq(\"d\", AnyExp.seq(IO\u003cJsBool\u003e cond1, IO\u003cJsBool\u003e cond2)\n                          \"e\", AllExp.par(IO\u003cJsBool\u003e cond2, IO\u003cJsBool\u003e cond3)\n                          \"f\", JsArrayExp.par(IO\u003cJsValue\u003e e5, IO\u003cJsValue\u003e e6)\n                          );\n\nJsObjExp exp = JsObjExp.par(\"a\",a,\n                            \"b\",b,\n                            \"c\",c\n                           );\n\nResult\u003cJsObj\u003e json = exp.compute();\n\n```\n\nHere are some key points about the code example:\n\n1. **Readability**: The code is relatively easy to read and understand, thanks to the fluent API\n   style provided by JIO's expressions. This makes it straightforward to follow the logic of\n   constructing a `JsObj` with multiple key-value pairs.\n\n2. **Modularity**: Each key-value pair is constructed separately, making it easy to add, modify, or\n   remove components without affecting the others. This modularity is a significant advantage when\n   dealing with complex data structures.\n\n3. **Parallelism**: The example demonstrates the ability to perform computations in parallel when\n   constructing  \n   the `JsObj`. By using expressions like `JsObjExp.par`, you can take advantage of multicore\n   processors and improve performance.\n\n4. **Nesting**: The example also shows that you can nest expressions within each other, allowing for\n   recursive data  \n   structures. This is valuable when dealing with deeply nested expressions or other complex data\n   formats.\n\nOverall, the code example effectively illustrates how JIO's expressions enable you to create,\nmanipulate, and compose functional effects to handle complex data scenarios. It highlights the\nconciseness and expressiveness of the library  \nwhen dealing with such tasks.\n\n### ListExp\n\nRepresents an expression that is reduced to a list of values. You can create ListExp expressions\nusing the `seq` method to evaluate effects sequentially or using the `par` method to evaluate\neffects in parallel. If one effect fails, the entire expression fails.\n\n```code\n\nListExp\u003cString\u003e par = ListExp.par(IO\u003cString\u003e effect1, IO\u003cString\u003e effect2, ...)\n\nListExp\u003cInteger\u003e seq = ListExp.seq(IO\u003cString\u003e effect3, IO\u003cString\u003e effect3, ...)\n\nResult\u003cList\u003cString\u003e\u003e xs = par.compute();\nResult\u003cList\u003cInteger\u003e\u003e ys = seq.compute();\n\n```\n\nIt's possible to create ListExp from stream of effects of the same type using the collectors\n`parCollector` and `seqCollector`:\n\n```code\n\nLambda\u003cString, Person\u003e getPersonFromId = ???;\n\nList\u003cString\u003e ids = ???;\n\nListExp\u003cPerson\u003e xs = ids.stream()\n                        .filter(id -\u003e id \u003e 0)\n                        .map(getPersonFromId)\n                        .collect(ListExp.parCollector());\n\nResult\u003cList\u003cPerson\u003e\u003e persons = xs.compute();\n\n```\n\n---\n\n## \u003ca name=\"Clocks\"\u003e\u003ca/\u003e Clocks\n\nIn functional programming, it's crucial to maintain a clear separation between inputs and outputs of\na function. When dealing with time-related operations, such as retrieving the current date or time,\nit becomes even more critical to adhere to this principle. This is where the concept of clocks in\nJIO comes into play. A clock in JIO is essentially a supplier that returns a numeric value,\nrepresenting time. There are three types of  \nclocks available:\n\n- Realtime: This clock is affected by Network Time Protocol (NTP) adjustments and can move both\n  forwards and backward in time. It is implemented using the System.currentTimeMillis() method.\n  Realtime clocks are typically used when you need to work with the current wall-clock time.\n- Monotonic: Monotonic clocks are useful for measuring time intervals and performing time-related\n  comparisons. They are not affected by NTP adjustments and provide a consistent and continuous time\n  source. Monotonic clocks are implemented using the System.nanoTime() method.\n- Custom: JIO allows you to create your custom clocks. Custom clocks are particularly valuable for\n  testing scenarios  \n  where you want to control the flow of time, possibly simulating the past or future.\n\n```code\n\nsealed interface Clock extends Supplier\u003cLong\u003e permits Monotonic,RealTime, CustomClock {}\n\n```\n\nEvery time you write _new Date()_ or _Instant.now()_ in the body of a method or function, you are\ncreating a side effect.  \nRemember that in FP, all the inputs must appear in the signature of a function. Dealing with time,\nit's even more  \nimportant. Also, it's impossible to control by any test the value of that timestamp which leads to\ncode difficult to  \ntest.\n\n### Why It Matters\n\nThe reason why dealing with time as an input is crucial in functional programming is to make code\nmore predictable,  \ntestable, and less error-prone. Consider the following scenario, which is a common source of bugs:\n\n**Bug Scenario**:\n\n```code\npublic class PaymentService {\n    public boolean processPayment(double amount) {\n        // Get the current date and time\n        Instant currentTime = Instant.now();\n\n        // Perform payment processing logic\n        // ...\n\n        // Check if the payment was made within a specific time window\n        Instant windowStart = currentTime.minus(Duration.ofHours(1));\n        Instant windowEnd = currentTime.plus(Duration.ofHours(1));\n\n        return paymentTime.isAfter(windowStart) \u0026\u0026 paymentTime.isBefore(windowEnd);\n    }\n}\n\n```\n\n**Better Version Using a Clock**  \nA better approach is to pass a clock as a method parameter:\n\n```code\n\npublic class PaymentService {\n    public boolean processPayment(double amount, Clock clock) {\n        // Get the current time from the provided clock\n        Instant currentTime = Instant.ofEpochMilli(clock.get());\n\n        // Perform payment processing logic\n        // ...\n\n        // Check if the payment was made within a specific time window\n        Instant windowStart = currentTime.minus(Duration.ofHours(1));\n        Instant windowEnd = currentTime.plus(Duration.ofHours(1));\n\n        return paymentTime.isAfter(windowStart) \u0026\u0026 paymentTime.isBefore(windowEnd);\n    }\n}\n\n\n```\n\nIn this improved version, we pass a Clock object as a parameter to the processPayment method. This\napproach offers  \nseveral advantages:\n\n- Testability: During testing, you can provide a custom clock that allows you to control the current\n  time, making tests more predictable and reliable.\n- Predictability: The behavior of the method is consistent regardless of when it's called since it\n  depends on the  \n  provided clock.\n\nBy using a clock as a parameter, you enhance the reliability and maintainability of your code,\nespecially in scenarios  \nwhere time plays a critical role.\n\n## \u003ca name=\"Debugging-and-JFR-integration\"\u003e\u003ca/\u003e Debugging and Java Flight Recorder (JFR) Integration\n\n### Why I chose JFR\n\nIn the world of Java, there has long been a multitude of logging libraries and frameworks, each with\nits strengths and limitations. However, the introduction of Java Flight Recorder (JFR) has been a\ngame-changer. JFR is a native and highly efficient profiling and event recording mechanism embedded\nwithin the Java Virtual Machine (JVM). Its native integration means it operates seamlessly with your\nJava applications, imposing minimal performance overhead. JFR provides unparalleled visibility into\nthe inner workings of your code, allowing you to capture and analyze events with precision.\n\nUnlike external logging libraries, JFR doesn't rely on third-party dependencies or introduce\nadditional complexity to  \nyour projects. By using JFR within JIO, you harness the power of this built-in tool to gain deep\ninsights into the  \nbehavior of your functional effects and expressions, all while keeping your codebase clean and\nefficient. JFR is the  \ndream solution for Java developers seeking robust debugging and monitoring capabilities with minimal\nhassle.\"\n\nDebugging and monitoring the behavior of your JIO-based applications is essential for\ntroubleshooting, performance  \noptimization, and gaining insights into your functional effects and expressions. JIO provides\ncomprehensive support for debugging and integration with Java Flight Recorder (JFR) to capture and\nanalyze events.\n\n---\n\n### Debugging Individual Effects\n\nYou can enable debugging for individual effects using the `debug` method. When this method is used,\na new effect is created that generates a `RecordedEvent` and sends it to the Flight Recorder system.\nYou can also customize the event by providing an `EventBuilder`. Here's an overview:\n\nThe `IO` class has the following methods for debugging:\n\n```code\npublic sealed abstract class IO\u003cOutput\u003e implements Callable\u003cResult\u003cOutput\u003e\u003e permits Exp, Val\n{\n\n    IO\u003cO\u003e debug();\n\n    IO\u003cO\u003e debug(EventBuilder\u003cO\u003e builder);\n\n}\n```\n\nThe resulting JFR event is defined as follows:\n\n```code\nimport jdk.jfr.*;\n\n@Label(\"Expression Evaluation Info\")\n@Name(\"jio.exp.EvalExp\")\n@Category({\"JIO\", \"EXP\"})\n@Description(\"Duration, output, context and other info related to a computation\")\n@StackTrace(value = false)\nclass ExpEvent extends Event {\n\n    @Label(\"exp\")\n    public String expression;\n\n    @Label(\"value\")\n    public String value;\n\n    @Label(\"context\")\n    public String context;\n\n    @Label(\"result\")\n    public String result;\n    public enum RESULT {SUCCESS, FAILURE}\n\n    @Label(\"exception\")\n    public String exception;\n\n}\n```\n\nYou can use the [JIO debugger](#junit) to print the events sent to the JFR system. Here's an\nexample:\n\n```code\n@RegisterExtension\nstatic Debugger debugger = Debugger of (Duration.ofSeconds(1));\n\n@Test\npublic void test() {\n\n    Result\u003cInteger\u003e value =\n        IO.succeed(10)\n          .debug()\n          .compute();\n\n    Result\u003cInteger\u003e failure =\n        IO.\u003cInteger\u003efail(new RuntimeException(\"JIO is great!\"))\n          .debug()\n          .compute();\n}\n```\n\nThe result includes events like this:\n\n```text\n------ eval-exp --------\n|  Expression: Val\n|  Result: SUCCESS\n|  Duration: 14,249 ms\n|  Output: 10\n|  Context:\n|  Thread: main\n|  Event Start Time: 2024-03-23T11:23:28.745121208+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: Val\n|  Result: FAILURE\n|  Duration: 39,583 µs\n|  Output: java.lang.RuntimeException: JIO is great!\n|  Context:\n|  Thread: main\n|  Event Start Time: 2024-03-23T11:23:28.759822+01:00\n-------------------------\n\n```\n\nThe event type is always \"eval-exp\" (for evaluation), and the expression is \"Val\" (for Value),\nreflecting the evaluation of two irreducible expressions. The result is \"SUCCESS\" for the first\nevaluation, and \"FAILURE\" for the second. The context in both cases is the default (an empty\nstring).\n\nYou can customize event messages using an `EventBuilder`. For example:\n\n```code\n\nEventBuilder\u003cInteger\u003e eb =\n    EventBuilder.\u003cInteger\u003eof(\"other_exp_name\", \"fun\")\n                .withSuccessOutput(output -\u003e \"XXX\")\n                .withFailureOutput(Throwable::getMessage);\n\nResult\u003cInteger\u003e value =\n    IO.succeed(10)\n      .debug(eb)\n      .compute();\n\nResult\u003cInteger\u003e failure =\n    IO.\u003cInteger\u003efail(new RuntimeException(\"JIO is great!\"))\n      .debug(eb)\n      .compute();\n\n```\n\nThe result with this customization is:\n\n```text\n------ eval-exp --------\n|  Expression: other_exp_name\n|  Result: SUCCESS\n|  Duration: 19,285 ms\n|  Output: XXX\n|  Context: fun\n|  Thread: main\n|  Event Start Time: 2024-03-23T11:26:56.386859042+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: other_exp_name\n|  Result: FAILURE\n|  Duration: 44,417 µs\n|  Output: JIO is great!\n|  Context: fun\n|  Thread: main\n|  Event Start Time: 2024-03-23T11:26:56.407043375+01:00\n-------------------------\n```\n\nThe `EventBuilder` provides key points for customization, including specifying event messages for\nsuccessful and failed computations and associating events with specific expressions and contexts.\n\n### Debugging Expressions\n\nJIO's debugging capabilities extend beyond individual effects. You can attach a debug mechanism to\neach operand of an expression using the `debugEach` method. This allows you to monitor and log the\nexecution of each operand individually. Here's an overview:\n\n```code\nsealed abstract class Exp\u003cOutput\u003e extends IO\u003cOutput\u003e\n    permits AllExp, AnyExp, CondExp, IfElseExp, JsArrayExp,\n            JsObjExp, ListExp, PairExp, SwitchExp, TripleExp {\n\n    Exp\u003cOutput\u003e debugEach(EventBuilder\u003cO\u003e builder);\n\n    Exp\u003cOutput\u003e debugEach(String context);\n\n}\n```\n\nBy using `debugEach`, you can gain insights into the behavior of complex expressions and identify\nany issues or bottlenecks that may arise during execution. This mechanism is recursive, meaning that\nif subexpressions are expressions themselves, `debugEach` will be called on them, copying the\ncontext of the event (if specified).\n\nYou can also provide an `EventBuilder` or a descriptive context to customize the debug events for\neach operand.\n\nHere's an example of using `debugEach`:\n\n```code\n\n  @RegisterExtension\n  static Debugger debugger = Debugger.of(Duration.ofSeconds(2));\n\n  @Test\n  public void testMain(){\n    Supplier\u003cBoolean\u003e isLowerCase = BoolGen.arbitrary().sample();\n    Supplier\u003cString\u003e lowerCase = Combinators.oneOf(\"a\", \"e\", \"i\", \"o\", \"u\").sample();\n    Supplier\u003cString\u003e upperCase = Combinators.oneOf(\"A\", \"E\", \"I\", \"O\", \"U\").sample();\n\n    SwitchExp\u003cString, String\u003e match =\n        SwitchExp.\u003cString, String\u003eeval(IfElseExp.\u003cString\u003epredicate(IO.lazy(isLowerCase))\n                                                .consequence(() -\u003e IO.lazy(lowerCase))\n                                                .alternative(() -\u003e IO.lazy(upperCase))\n                                      )\n                 .matchList(List.of(\"a\", \"e\", \"i\", \"o\", \"u\"),\n                            letter -\u003e IO.succeed(\"%s %s\".formatted(letter, letter.toUpperCase())),\n                            List.of(\"A\", \"E\", \"I\", \"O\", \"U\"),\n                            letter -\u003e IO.succeed(\"%s %s\".formatted(letter, letter.toLowerCase()))\n                           )\n                 .debugEach(\"context\");\n\n  }\n```\n\nThe result after executing this test includes events related to each operand:\n\n```text\n\n------ eval-exp --------\n|  Expression: SwitchExp-eval-predicate\n|  Result: SUCCESS\n|  Duration: 13,613 ms\n|  Output: false\n|  Context: context\n|  Thread: main\n|  Event Start Time: 2024-03-23T12:31:42.229576959+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: SwitchExp-eval-alternative\n|  Result: SUCCESS\n|  Duration: 21,000 µs\n|  Output: O\n|  Context: context\n|  Thread: main\n|  Event Start Time: 2024-03-23T12:31:42.244025125+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: SwitchExp-eval\n|  Result: SUCCESS\n|  Duration: 15,562 ms\n|  Output: O\n|  Context: context\n|  Thread: main\n|  Event Start Time: 2024-03-23T12:31:42.228762834+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: SwitchExp-branch[1]\n|  Result: SUCCESS\n|  Duration: 7,583 µs\n|  Output: O o\n|  Context: context\n|  Thread: main\n|  Event Start Time: 2024-03-23T12:31:42.244515667+01:00\n-------------------------\n\n------ eval-exp --------\n|  Expression: SwitchExp\n|  Result: SUCCESS\n|  Duration: 15,772 ms\n|  Output: O o\n|  Context: context\n|  Thread: main\n|  Event Start Time: 2024-03-23T12:31:42.228759417+01:00\n-------------------------\n```\n\nAs mentioned earlier, the `debugExp` function is recursive. Since the `eval` subexpression of the\n`SwitchExp` is an `IfElseExp`, you can see events associated with it: `SwitchExp-eval-predicate` and\n`SwitchExp-eval-alternative`. On the other hand you can see how the first branch of the Switch was\nreturned.\n\n## \u003ca name=\"exp-installation\"\u003e\u003ca/\u003e Installation\n\nIt requires Java 21 or greater\n\n```code\n\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.github.imrafaelmerino\u003c/groupId\u003e\n    \u003cartifactId\u003ejio-exp\u003c/artifactId\u003e\n    \u003cversion\u003e3.0.0-RC2\u003c/version\u003e\n\u003c/dependency\u003e\n\n```\n\n[json-values](https://github.com/imrafaelmerino/json-values) is the only dependency\n\n## \u003ca name=\"jio-http\"\u003e\u003ca/\u003e jio-http\n\n[![Maven](https://img.shields.io/maven-central/v/com.github.imrafaelmerino/jio-http/3.0.0-RC2)](https://search.maven.org/artifact/com.github.imrafaelmerino/jio-http/3.0.0-RC2/jar\n\"jio-http\")\n\n### \u003ca name=\"httpserver\"\u003e\u003ca/\u003e HTTP server\n\nIn JIO, you can build and deploy HTTP servers using the `HttpServerBuilder`. This builder is a\nversatile tool for defining and launching HTTP servers for various purposes, including testing. The\n`HttpServerBuilder` allows you to create `HttpServer` or `HttpsServer` instances with ease.\n\nEmploying the `HttpServer` native class to initiate servers for your tests simplifies both setup and\nteardown procedures, as the server is embedded within the Java process running the test. **This\nensures that you'll never leave a port lingering**.\n\n**Specifying the Request Handlers**\n\nTo handle specific URI paths, you can associate each path with an HTTP request handler. For each\npath, specify a handler that will be invoked for incoming requests.\n\n```code\nHttpHandler handler = ???;\n\nHttpHandler handler1 = ???;\n\nHttpServerBuilder serverBuilder = HttpServerBuilder.of(\"/your-path\", handler,\n                                                       \"/your-path1\", handler1);\n\n```\n\n**Specifying an Executor**\n\nWhen creating an `HttpServer` is possible to specify an `Executor`. All HTTP requests received by\nthe server will be handled in tasks provided to this executor. By default, virtual threads are used:\n`Executors.newVirtualThreadPerTaskExecutor()`\n\n**Setting the Socket Backlog**\n\nThe `HttpServerBuilder` allows you to specify the socket backlog, which defines the number of\nincoming connections that can be queued for acceptance. You can set the backlog using the\n`withBacklog(int backlog)` method.\n\n```code\nint backlog = ???; // Your desired backlog value\n\nserverBuilder.withBacklog(backlog);\n```\n\n**Enabling SSL**\n\nIf you want to accept only SSL connections:\n\n```code\n\nHttpsConfigurator httpsConfigurator = ???;\n\nserverBuilder.withSSL(httpsConfigurator);\n\n```\n\n**Recording JFR Events (Java Flight Recorder)**\n\nBy default, the `HttpServer` records Java Flight Recorder (JFR) events for HTTP requests, which can\nbe helpful for debugging and performance analysis. However, you can disable this feature:\n\n```code\n\nserverBuilder.withoutRecordedEvents();\n\n```\n\n**Starting the server on a Specific Port**\n\nThe start methods allow you to create and start the HTTP server at your convenience.\n\n```code\nString host = \"localhost\";\nint port = 8080;\n\nResult\u003cHttpServer\u003e server = serverBuilder.start(host, port);\n\n```\n\n**Starting the server on a Random Available Port**\n\nYou can even pick a random port, which is useful for local testing as we'll see later.\n\n```code\nint startPort = 8000;\n\nint endPort = 9000;\n\nResult\u003cHttpServer\u003e = serverBuilder.startAtRandom(startPort, endPort);\n\n```\n\nIn conclusion, with the `HttpServerBuilder`, you can easily create and deploy HTTP/HTTPS servers in\nyour JIO applications, making it convenient for testing and development. Whether you need to specify\nan executor, add request handlers, or start on specific or random ports, this builder provides the\nflexibility and functionality to meet your server deployment needs.\n\nFind below a complete example and the events sent to the JFR system:\n\n```code\n import com.sun.net.httpserver.HttpHandler;\n\n HttpHandler tokenHandler =\n            PostStub.of(BodyStub.gen(JsObjGen.of(\"access_token\", JsStrGen.alphanumeric(10, 10))\n                                             .map(JsObj::toString)),\n                        StatusCodeStub.cons(200)\n                        );\n\n HttpHandler thankHandler =\n        GetStub.of(BodyStub.cons(\"your welcome!\"),\n                   StatusCodeStub.gen(Combinators.freq(Pair.of(5, IntGen.arbitrary(200, 299)),\n                                                       Pair.of(1, Gen.cons(401))))\n                  );\n\n Result\u003cHttpServer\u003e server =\n        HttpServerBuilder.of(Map.of(\"/token\", tokenHandler,\n                                    \"/thanks\", thankHandler\n                                    )\n                            )\n                         .startAtRandom(8000, 9000);\n\n```\n\nThe example code sets up a test environment for an HTTP client with OAuth support (Client\nCredentials flow). It uses stubs from [jio-test](#jio-test) to create HTTP handlers for testing\ndifferent scenarios. The `tokenHandler` simulates an OAuth token request, and the `thankHandler`\nsimulates a response that includes a \"your welcome!\" message. The status code for the `thankHandler`\nis generated to return a 401 response approximately 1 out of 6 times, simulating the case where the\naccess token has expired. The `HttpServerBuilder` is used to create an HTTP server on a random port\nto handle these requests. This setup allows testing of various scenarios, including token expiration\nhandling.\n\n---\n\n### \u003ca name=\"httpclient\"\u003e\u003ca/\u003e HTTP client\n\nIn JIO, I create an HTTP client on top of the Java HttpClient introduced in Java 11. JIO's goal is\nto work with Java's native objects (no abstraction on top of them) while treating errors as normal\nvalues (Lambdas can help us here!). This approach allows us to define an HTTP exchange as a\nfunction, where the input is an HTTP request (modeled using `java.net.http.HttpRequest.Builder`),\nand the output is an `IO` object representing the response. The function signature for this is as\nfollows:\n\n```code\n\n\u003cO\u003e Lambda\u003cHttpRequest.Builder, HttpResponse\u003cO\u003e\u003e\n\n```\n\nTo make this type more concise, we give it an alias in JIO-HTTP. We call the previous function an\n`HttpLambda\u003cO\u003e`, where `O` represents the response body type (typically `String` or `byte[]`):\n\n```code\n\ninterface HttpLambda\u003cRespBody\u003e extends Lambda\u003cHttpRequest.Builder, HttpResponse\u003cRespBody\u003e\u003e {}\n\n```\n\nJIO-HTTP offers an HTTP client with various options for handling different response types. Depending\non your desired response type, you can use one of the following methods:\n\n```java\n\npublic interface JioHttpClient {\n\n  HttpLambda\u003cString\u003e ofString();\n\n  HttpLambda\u003cbyte[]\u003e ofBytes();\n\n  HttpLambda\u003cVoid\u003e discarding();\n\n  \u003cRespBody\u003e HttpLambda\u003cRespBody\u003e bodyHandler(HttpResponse.BodyHandler\u003cRespBody\u003e handler);\n\n}\n\n\n```\n\nYou can create and configure a `JioHttpClient` using the builder `JioHttpClientBuilder`. This\nbuilder allows you to customize the HTTP client, including specifying a retry policy, a retry\npredicate for selecting what errors to retry, and enabling or disabling the recording of Java Flight\nRecorder (JFR) events for HTTP requests and responses. JFR event recording is enabled by default:\n\n- `withRetryPolicy`: Sets a default retry policy for handling exceptions during requests.\n- `withRetryPredicate`: Sets a default predicate for selectively applying the retry policy based on\n  the type or condition of the exception.\n- `withoutRecordEvents`: Disables the recording of JFR events for HTTP requests.\n\nBelow is a complete example, making requests to the famous PetStore service, illustrating how to use\ncreate and use the JIO HTTP client.\n\n```code\n\npublic class TestHttpClient {\n\n  @RegisterExtension\n  static Debugger debugger = Debugger.of(Duration.ofSeconds(2));\n\n  static JioHttpClient client =\n      JioHttpClientBuilder.of(HttpClient.newBuilder()\n                                        .connectTimeout(Duration.ofMillis(300))\n                             )\n                          .withRetryPolicy(RetryPolicies.incrementalDelay(Duration.ofMillis(10))\n                                                        .append(RetryPolicies.limitRetries(5)))\n                          .withRetryPredicate(IS_CONNECTION_TIMEOUT.or(IS_CONNECTION_REFUSE))\n                          .get();\n\n  static String uri = \"https://petstore.swagger.io/v2/%s/%s\";\n\n  static BiFunction\u003cString, String, Builder\u003e GET =\n      (entity, id) -\u003e HttpRequest.newBuilder()\n                                 .GET()\n                                 .uri(URI.create(uri.formatted(entity,\n                                                               id)));\n\n\n  @Test\n  public void testGetPetStoreMethods() {\n\n    IO\u003cHttpResponse\u003cString\u003e\u003e getPet = client.ofString()\n                                            .apply(GET.apply(\"pet\",\n                                                             \"1\"));\n\n    IO\u003cHttpResponse\u003cString\u003e\u003e getOrder = client.ofString()\n                                              .apply(GET.apply(\"store/order\",\n                                                               \"1\"));\n\n    Result\u003cList\u003cInteger\u003e\u003e result = ListExp.par(getPet,\n                                               getOrder)\n                                          .map(responses -\u003e responses.stream()\n                                                                     .map(HttpResponse::statusCode)\n                                                                     .toList()\n                                              )\n                                          .compute();\n\n    Assertions.assertTrue(result.isSuccess() \u0026\u0026 result.getOutput().size() == 2);\n\n  }\n\n```\n\nA possible outcome is:\n\n```text\nStarted JFR stream for 2,000 sg in Properties\n\n------ httpclient-req -----\n|  Result: FAILURE\n|  Exception: java.net.ConnectException: HTTP connect timed out\n|  Duration: 331,717 ms\n|  Method: GET\n|  URI Host: petstore.swagger.io\n|  URI Path: /v2/pet/1\n|  Request Counter: 1\n|  Thread: virtual--1\n|  Event Start Time: 2024-03-23T11:52:48.919435Z\n----------------------\n\n------ httpclient-req -----\n|  Result: FAILURE\n|  Exception: java.net.ConnectException: HTTP connect timed out\n|  Duration: 331,716 ms\n|  Method: GET\n|  URI Host: petstore.swagger.io\n|  URI Path: /v2/store/order/1\n|  Request Counter: 2\n|  Thread: virtual--1\n|  Event Start Time: 2024-03-23T11:52:48.919444083Z\n----------------------\n\n------ httpclient-req -----\n|  Result: CLIENT_ERROR\n|  Status Code: 404\n|  Duration: 415,771 ms\n|  Method: GET\n|  URI Host: petstore.swagger.io\n|  URI Path: /v2/store/order/1\n|  Request Counter: 4\n|  Thread: virtual--1\n|  Event Start Time: 2024-03-23T11:52:49.271747042Z\n----------------------\n\n------ httpclient-req -----\n|  Result: SUCCESS\n|  Status Code: 200\n|  Duration: 417,817 ms\n|  Method: GET\n|  URI Host: petstore.swagger.io\n|  URI Path: /v2/pet/1\n|  Request Counter: 3\n|  Thread: virtual--1\n|  Event Start Time: 2024-03-23T11:52:49.271737625Z\n----------------------\n\n```\n\nSome errors occurred due to the connection timeout being too short for this particular scenario.\nThankfully, the retry mechanism came to the rescue! Additionally, the `HttpExceptions` class\nprovides numerous predicates to help identify the most common errors that can occur during request\nexecution. As you can see in the thread field, jio-http client uses virtual threads.\n\n---\n\n### \u003ca name=\"oauth\"\u003e\u003ca/\u003e OAUTH HTTP client\n\njio-http provides support for client credentials flow OAuth. Here are the possible customizations\nfor the `ClientCredentialsBuilder` builder:\n\n1. The request sent to the server to get the access token:\n\n- `accessTokenReq` parameter: A lambda that takes the regular HTTP client and returns the HTTP\n  request to get the token. There is a factory method to build a specific request in the class\n  `AccessTokenRequest`. For example one that takes in the client id and secret and an `URI` to make\n  the following request:\n\n  ```shell\n\n  curl -X POST -H \"Accept: application/json\" \\\n               -H \"Authorization: Basic ${Base64(ClientId:ClientSecret)}\" \\\n               -H \"Content-Type: application/x-www-form-urlencoded\" \\\n               -d \"grant_type=client_credentials\" \\\n               https://host:port/token\n\n  ```\n\n2. A function to read the access token from the server response:\n\n- `getAccessToken` parameter: A lambda that takes the server response and returns the OAuth token.\n  You can use the existing implementation `GetAccessToken`, which parses the response into a `JsObj`\n  and returns the access token located at the \"access_token\" field. If the token is not found, the\n  lambda fails with the exception `AccessTokenNotFound`. The `GetAccessToken` class is a singleton\n  with a private constructor, and you can use the `GetAccessToken.DEFAULT` instance for this\n  purpose.\n\n3. A predicate that checks if the access token needs to be refreshed:\n\n- `refreshTokenPredicate` parameter: A predicate that checks the response to determine if the access\n  token needs to be refreshed.\n\n4. The authorization header name:\n\n- `authorizationHeaderName` field: The name of the authorization header, which is set to\n  \"Authorization\" by default.\n\n5. A function to create the authorization header value from the access token:\n\n- `authorizationHeaderValue` field: A function that takes the access token and returns the\n  authorization header value. By default, it is set to \"Bearer ${Access Token}\".\n\nYou can customize these options when creating an instance of `ClientCredentialsBuilder` to configure\nthe behavior of the OAuth client credentials flow support in your HTTP client. Since you need a\nJioHttpClientBuilder instance to create `ClientCredentialsBuilder`, you can specify retry policies\nand predicates, and of course you can disable the recording of JFR events for every exchange.\n\nThe builder returns an instance of `ClientCredentialsBuilder`, which is an implementation of\n`OauthHttpClient`:\n\n```java\npackage jio.http.client.oauth;\n\nimport jio.http.client.HttpLambda;\nimport jio.http.client.JioHttpClient;\nimport java.net.http.HttpResponse;\n\npublic interface OauthHttpClient extends JioHttpClient {\n\n  // since it extends JioHttpClient: ofString() ofBytes() and so on are available as well!\n\n  HttpLambda\u003cString\u003e oauthOfString();\n\n  HttpLambda\u003cbyte[]\u003e oauthOfBytes();\n\n  HttpLambda\u003cVoid\u003e oauthDiscarding();\n\n  \u003cRespBody\u003e HttpLambda\u003cRespBody\u003e oauthBodyHandler(final HttpResponse.BodyHandler\u003cRespBody\u003e handler);\n\n}\n\n\n```\n\nThe advantage of the `oauthXXX` methods is that they handle all token retrieval and refresh requests\non behalf of the client, relieving developers from the burden of implementing these processes.\n\nHere's an illustrative example, where I created an http server and two stubs:\n\n```code\nimport java.net.URI;\n\npublic class TestOauthHttpClient {\n\n  @RegisterExtension\n  Debugger debugger = Debugger.of(Duration.ofSeconds(2));\n\n  int PORT = 7777;\n  Result\u003cHttpServer\u003e server;\n  {\n    BodyStub getTokenBodyStub = serverReqCounter -\u003e reqBody -\u003e reqUri -\u003e reqHeaders -\u003e\n        JsObj.of(\"access_token\", JsStr.of(String.valueOf(serverReqCounter))).toString();\n\n    StatusCodeStub getTokenStatusCodeStub = StatusCodeStub.cons(200);\n\n    BodyStub handlerBodyStub = serverReqCounter -\u003e body -\u003e uri -\u003e headers -\u003e\n        serverReqCounter == 2 ? \"\" : String.valueOf(serverReqCounter);\n\n    StatusCodeStub handlerStatusCodeStub =\n        serverReqCounter -\u003e body -\u003e uri -\u003e headers -\u003e serverReqCounter == 2 ? 401 : 200;\n\n    server = HttpServerBuilder.of(Map.of(\"/token\",\n                                         PostStub.of(getTokenBodyStub,\n                                                     getTokenStatusCodeStub\n                                                    ),\n                                         \"/thanks\",\n                                         GetStub.of(handlerBodyStub,\n                                                    handlerStatusCodeStub\n                                                   )\n                                        )\n                                 )\n                              .start(PORT);\n  }\n\n  JioHttpClientBuilder clientBuilder =\n      JioHttpClientBuilder.of(HttpClient.newBuilder()\n                                        .connectTimeout(Duration.ofMillis(300)))\n                          .withRetryPolicy(RetryPolicies.incrementalDelay(Duration.ofMillis(10))\n                                                        .append(RetryPolicies.limitRetries(5)))\n                          .withRetryPredicate(IS_CONNECTION_TIMEOUT.or(IS_CONNECTION_REFUSE));\n\n  URI tokenUri = URI.create(\"http://localhost:%s/token\".formatted(7777));\n\n  OauthHttpClient client =\n      ClientCredentialsBuilder.of(clientBuilder,\n                                  AccessTokenRequest.of(\"client_id\",\n                                                        \"client_secret\",\n                                                        tokenUri\n                                                       ),\n                                  GetAccessToken.DEFAULT,\n                                  //token in access_token key in a JSON\n                                  resp -\u003e resp.statusCode() == 401\n                                  // if 401 go for a new token\n                                 )\n                              .get();\n\n  @Test\n  public void testOuth() {\n    URI uri = URI.create(\"http://localhost:%s/thanks\".formatted(PORT));\n    Assertions.assertEquals(new Success\u003c\u003e(\"4\"),\n                            client.oauthOfString()\n                                  .apply(HttpRequest.newBuilder()\n                                                    .uri(uri))\n                                  .compute()\n                                  .map(resp -\u003e resp.body())\n                           );\n\n  }\n\n}\n\n```\n\nLet's pick some events from the console. Notice that both the events from the server and the client\nare printed.\n\n```text\n\n------ httpserver-req -----\n| Result: SUCCESS\n| Status Code: 200\n| Duration: 29,349 ms\n| Protocol: HTTP/1.1\n| Method: POST\n| URI: /token\n| Request Counter: 1\n| Remote Host Address: localhost\n| Remote Host Port: 52625\n| Headers: Accept:application/json, Connection:Upgrade, HTTP2-Settings, Http2-settings:AAEAAEAAAAIAAAABAAMAAABkAAQBAAAAAAUAAEAA, Host:localhost:7777, Upgrade:h2c, User-agent:Java-http-client/21.0.2, Authorization:Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=, Content-type:application/x-www-form-urlencoded, Content-length:29\n| Thread: virtual--1\n| Event Start Time: 2024-03-23T12:06:20.820267708Z\n----------------------\n\n------ httpclient-req -----\n|  Result: SUCCESS\n|  Status Code: 200\n|  Duration: 69,062 ms\n|  Method: POST\n|  URI Host: localhost\n|  URI Path: /token\n|  Request Counter: 1\n|  Thread: main\n|  Event Start Time: 2024-03-23T12:06:20.785406958Z\n----------------------\n\n------ httpserver-req -----\n| Result: CLIENT_ERROR\n| Status Code: 401\n| Duration: 552,542 µs\n| Protocol: HTTP/1.1\n| Method: GET\n| URI: /thanks\n| Request Counter: 2\n| Remote Host Address: localhost\n| Remote Host Port: 52625\n| Headers: Connection:Upgrade, HTTP2-Settings, Http2-settings:AAEAAEAAAAIAAAABAAMAAABkAAQBAAAAAAUAAEAA, Host:localhost:7777, User-agent:Java-http-client/21.0.2, Upgrade:h2c, Authorization:Bearer 1\n| Thread: virtual--1\n| Event Start Time: 2024-03-23T12:06:20.865515083Z\n----------------------\n\n------ httpclient-req -----\n|  Result: CLIENT_ERROR\n|  Status Code: 401\n|  Duration: 2,182 ms\n|  Method: GET\n|  URI Host: localhost\n|  URI Path: /thanks\n|  Request Counter: 2\n|  Thread: main\n|  Event Start Time: 2024-03-23T12:06:20.864685333Z\n----------------------\n\n------ httpserver-req -----\n| Result: SUCCESS\n| Status Code: 200\n| Duration: 153,750 µs\n| Protocol: HTTP/1.1\n| Method: POST\n| URI: /token\n| Request Counter: 3\n| Remote Host Address: localhost\n| Remote Host Port: 52625\n| Headers: Accept:application/json, Connection:Upgrade, HTTP2-Settings, Http2-settings:AAEAAEAAAAIAAAABAAMAAABkAAQBAAAAAAUAAEAA, Host:localhost:7777, Upgrade:h2c, User-agent:Java-http-client/21.0.2, Authorization:Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=, Content-type:application/x-www-form-urlencoded, Content-length:29\n| Thread: virtual--1\n| Event Start Time: 2024-03-23T12:06:20.867587833Z\n----------------------\n\n------ httpclient-req -----\n|  Result: SUCCESS\n|  Status Code: 200\n|  Duration: 1,090 ms\n|  Method: POST\n|  URI Host: localhost\n|  URI Path: /token\n|  Request Counter: 3\n|  Thread: main\n|  Event Start Time: 2024-03-23T12:06:20.866968542Z\n----------------------\n\n------ httpserver-req -----\n| Result: SUCCESS\n| Status Code: 200\n| Duration: 118,459 µs\n| Protocol: HTTP/1.1\n| Method: GET\n| URI: /thanks\n| Request Counter: 4\n| Remote Host Address: localhost\n| Remote Host Port: 52625\n| Headers: Connection:Upgrade, HTTP2-Settings, Http2-settings:AAEAAEAAAAIAAAABAAMAAABkAAQBAAAAAAUAAEAA, Host:localhost:7777, User-agent:Java-http-client/21.0.2, Upgrade:h2c, Authorization:Bearer 3\n| Thread: virtual--1\n| Event Start Time: 2024-03-23T12:06:20.868637833Z\n----------------------\n\n------ httpclient-req -----\n|  Result: SUCCESS\n|  Status Code: 200\n|  Duration: 933,667 µs\n|  Method: GET\n|  URI Host: localhost\n|  URI Path: /thanks\n|  Request Counter: 4\n|  Thread: main\n|  Event Start Time: 2024-03-23T12:06:20.868134875Z\n----------------------\n\n\n```\n\nIn the server event generated during the token request, you can observe the Authorization header\nsent by the client, with the value \"Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=\". If we decode this value\nfrom Base64, we obtain \"client_id: client_secret,\" which corresponds to the exact values we provided\nwhen configuring the `ClientCredsBuilder`.\n\n---\n\n### \u003ca name=\"http-installation\"\u003e\u003ca/\u003e Installation\n\nIt requires Java 21 or greater\n\n```code\n\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.github.imrafaelmerino\u003c/groupId\u003e\n    \u003cartifactId\u003ejio-http\u003c/artifactId\u003e\n    \u003cversion\u003e3.0.0-RC2\u003c/version\u003e\n\u003c/dependency\u003e\n\n```\n\n---\n\n## \u003ca name=\"jio-test\"\u003e\u003ca/\u003e jio-test\n\n[![Maven](https://img.shields.io/maven-central/v/com.github.imrafaelmerino/jio-test/3.0.0-RC2)](https://search.maven.org/artifact/com.github.imrafaelmerino/jio-test/3.0.0-RC2/jar\n\"jio-test\")\n\n### \u003ca name=\"junit\"\u003e\u003ca/\u003e Junit integration\n\nTo enable debugging Jio provides a JUnit exten","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fimrafaelmerino%2Fjio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fimrafaelmerino%2Fjio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fimrafaelmerino%2Fjio/lists"}