{"id":36622413,"url":"https://github.com/soabase/soabase-stages","last_synced_at":"2026-01-12T09:26:44.743Z","repository":{"id":57736239,"uuid":"94047835","full_name":"soabase/soabase-stages","owner":"soabase","description":"A tiny library that makes staged/pipelined CompletableFutures much easier to create and manage","archived":false,"fork":false,"pushed_at":"2018-03-02T17:50:23.000Z","size":66,"stargazers_count":25,"open_issues_count":0,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-07-05T08:16:52.661Z","etag":null,"topics":["concurrency","futures","java","java-8","java-9","threading"],"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/soabase.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-06-12T02:34:52.000Z","updated_at":"2023-10-04T00:20:01.000Z","dependencies_parsed_at":"2022-08-24T01:10:54.445Z","dependency_job_id":null,"html_url":"https://github.com/soabase/soabase-stages","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/soabase/soabase-stages","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soabase%2Fsoabase-stages","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soabase%2Fsoabase-stages/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soabase%2Fsoabase-stages/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soabase%2Fsoabase-stages/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/soabase","download_url":"https://codeload.github.com/soabase/soabase-stages/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soabase%2Fsoabase-stages/sbom","scorecard":{"id":835321,"data":{"date":"2025-08-11","repo":{"name":"github.com/soabase/soabase-stages","commit":"26c6b68d8c0b7f7cf0f82eb43dce8d97e0c5793c"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/29 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 2 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-23T18:51:58.166Z","repository_id":57736239,"created_at":"2025-08-23T18:51:58.166Z","updated_at":"2025-08-23T18:51:58.166Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28337713,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T06:09:07.588Z","status":"ssl_error","status_checked_at":"2026-01-12T06:05:18.301Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["concurrency","futures","java","java-8","java-9","threading"],"created_at":"2026-01-12T09:26:44.669Z","updated_at":"2026-01-12T09:26:44.732Z","avatar_url":"https://github.com/soabase.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# soabase-stages\n\n[![Build Status](https://travis-ci.org/soabase/soabase-stages.svg?branch=master)](https://travis-ci.org/soabase/soabase-stages)\n[![Maven Central](https://img.shields.io/maven-central/v/io.soabase.stages/soabase-stages.svg)](http://search.maven.org/#search%7Cga%7C1%7Csoabase-stages)\n\nA tiny library that makes staged/pipelined CompletableFutures much easier to create and manage.\n\n## Use Cases\n\n- You have a sequence of tasks that pipeline or chain\n- These tasks can be executed **synchronously** or **asynchronously**\n- You might need to **abort/cancel** the chain in the middle\n- You might need to provide a **timeout** for the tasks\n\nMost of this can be done with Java 8's CompletableFuture/CompletionStage today. Timeouts\nmust be added manually (or wait for Java 9). Aborting tasks is not supported. Also, the\nCompletableFuture/CompletionStage API is awkward and difficult to use.\n\n## StagedFuture\n\n`StagedFuture` simplifies CompletableFuture/CompletionStage so that you can write code like this:\n\n```java\nStagedFuture.async(executor)\n    .thenIf(() -\u003e queryDatabaseFor(\"something\"))\n        .withTimeout(Duration.ofSeconds(25))\n    .thenIf(record -\u003e applyRecord(record)) // chain aborts if no record found\n    .thenIf(result -\u003e returnNextRecord(result))\n    .whenSucceeded(nextResult -\u003e handleResult(nextResult))\n    .whenAborted(() -\u003e handleAbort())\n    .whenFailed(e -\u003e handleFailure(e));\n``` \n\n### Benefits Over Raw CompletableFuture/CompletionStage\n\n- Can easily set timeouts and timeouts with default values for tasks (without waiting for Java 9)\n- Allows setting the executor once instead of with each chained call\n- Allows a task to signal that the remainder of the chain should be canceled\n- Simplified API\n\nNote: you can easily access the managed `CompletionStage` when needed by calling `StagedFuture#unwrap()`.\n\n### Using Stages\n\nStages is available from [Maven Central](http://search.maven.org/#search%7Cga%7C1%7Csoabase-stages). Use your favorite build tool and specify:\n\n| GroupId | ArtifactId |\n|---------|------------|\n| io.soabase.stages | soabase-stages |\n\n[Change Log](https://github.com/soabase/soabase-stages/blob/master/CHANGELOG.md)\n\n#### Starting a chain\n\nSimilarly to the builders in `CompletableFuture` you start a chain using the builders in `StagedFuture`. There are syncrhonous and asynchronous builders:\n\n- `StagedFuture.sync()` - starts a StagedFuture chain that executes tasks synchronously\n- `StagedFuture.async(executor)` - starts a StagedFuture chain that executes tasks asynchronously using the given executor\n- `StagedFuture.asyncPool()` - starts a StagedFuture chain that executes tasks asynchronously using the ForkJoin pool\n\n#### Adding tasks to the chain\n\nTasks are added to the chain using one of the \"thenIf\" methods. The first task added is specified via a supplier and subsequent tasks are specified via functions that take the result of the previous task:\n\n_Initial Task_\n\n- `thenIf(Supplier\u003cOptional\u003cU\u003e\u003e proc)` - Execute the given task synchronously or asynchronously depending on how the StagedFuture was built. The given task returns an optional value that indicates whether or not the next stage can execute. If `Optional.empty()` is returned, the entire StagedFuture chain is considered to be aborted and no future tasks will execute. The `StagedFuture.whenAborted()` completer will get called.\n\n_Subsequent Tasks_\n\n- `thenIf(Function\u003cT, Optional\u003cU\u003e\u003e proc)` - If the chain has not been aborted or errored, the result of the current task is passed to this new task synchronously or asynchronously depending on how the StagedFuture was built. The given task returns an optional value that indicates whether or not the next stage can execute. If `Optional.empty()` is returned, the entire StagedFuture chain is considered to be aborted and no future tasks will execute. The `StagedFuture.whenAborted()` completer will get called.\n\n_Timeouts_\n\nThe \"then\" methods (see above) can optional be assigned a timeout or a timeout and default value:\n\n- `thenIf(X).withTimeout(Duration timeout)` - Sets a timeout for this stage's task. If the given timeout elapses before the task completes this stage is completed exceptionally with a `TimeoutException`.\n- `thenIf(X).withTimeout(Duration timeout, Supplier\u003cT\u003e defaultValue)` - Sets a timeout for this stage's task. If the given timeout elapses before the task completes this stage is completed with the given default value.\n\n_Completers_\n\nAt any point in the chain, you can add handlers for successful completions, failures or aborts:\n\n- `whenSucceeded(Consumer\u003cT\u003e handler)` - if the chain completes successfully the handler is called.\n- `whenSucceededYield(Function\u003cT, U\u003e handler)` - same as `whenSucceeded()` but allows mapping the return type.\n- `whenAborted(Runnable handler)` - if the chain is aborted (i.e. one of the `thenIf()` tasks returns empty) the handler is called.\n- `whenFailed(Consumer\u003cThrowable\u003e handler)` - if there is an exception or failure in the chain the handler is called.\n- `whenFinal(Runnable handler)` - calls the handler when the chain completes in any way (success, abort, exception, etc.).\n\n_Chaining Other Stages_\n\nYou can include external stages into the chain:\n\n- `thenStageIf(Function\u003cT, CompletionStage\u003cOptional\u003cU\u003e\u003e\u003e stage)` - executes the given stage asynchronously as the next task in the chain. If the stage returns an empty Optional the chain is aborted.\n\n_Access The Internal CompletionStage_\n\nYou can access the internally managed `CompletionStage` via:\n\n- `unwrap()` - returns the `CompletionStage\u003cOptional\u003cT\u003e\u003e`.\n\n### Tracing\n\nThe tasks submitted to StagedFuture can optionally be traced via the `Tracing` interface. The library comes with an SLF4J tracer and a `System.out` tracer. You can also write your own. Pass an instace of the tracer to the StagedFuture builder. E.g.\n\n```java\nStagedFuture.async(executor, Tracing.debug(logger)).\n    then(...)\n    ...\n```\n\n#### Cancelable Tracer\n\nThe special purpose tracer, `Cancelable`, can be used to enable canceling a running chain.\nIt keeps track of the threads in use by the StagedFuture it is associated with. At any time you can call cancelChain(boolean) to interrupt currently running tasks and prevent new tasks from running. E.g.\n\n```java\nCancelable cancelable = new Cancelable();\nStagedFuture.async(executor, cancelable)\n    .thenIf(() -\u003e worker(\"1\"))\n    .thenIf(s -\u003e hangingWorker(\"2\"))\n    .thenIf(s -\u003e worker(\"3\"))\n    .thenIf(s -\u003e worker(\"4\"));\n\ncancelable.cancel(true);    // hangingWorker() gets interrupted \n```\n\n### Manual Wrappers\n\nThe CompletionStage wrappers that StagedFuture uses internally can be used directly without having to use `StagedFuture`.\n\n#### Timeout\n\nThe `Timeout` class has methods that wrap `CompletionStage` adding timeouts and timeouts with default values. It roughly emulates the forthcoming Java 9 timeout features for CompletableFuture.\n\n#### Aborted\n\nThe `Aborted` class has methods that wrap `CompletionStage` and call given handlers when the stage completes with an empty `Optional`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoabase%2Fsoabase-stages","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsoabase%2Fsoabase-stages","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoabase%2Fsoabase-stages/lists"}