{"id":31029971,"url":"https://github.com/cowwoc/pouch","last_synced_at":"2025-09-13T22:57:24.621Z","repository":{"id":44944769,"uuid":"249260102","full_name":"cowwoc/pouch","owner":"cowwoc","description":"Inversion of Control that's designed for ease of use and debugging","archived":false,"fork":false,"pushed_at":"2025-06-19T00:50:12.000Z","size":4781,"stargazers_count":20,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-18T04:47:08.601Z","etag":null,"topics":["dependency-injection","inversion-of-control","java","service-locator","variable-scope"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cowwoc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":null,"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,"zenodo":null},"funding":{"github":"cowwoc"}},"created_at":"2020-03-22T19:47:55.000Z","updated_at":"2025-07-02T15:39:32.000Z","dependencies_parsed_at":"2022-09-24T22:12:24.949Z","dependency_job_id":"eba8f1f7-5fa1-4c15-a4c7-a2bf1a6f1d28","html_url":"https://github.com/cowwoc/pouch","commit_stats":null,"previous_names":[],"tags_count":35,"template":false,"template_full_name":null,"purl":"pkg:github/cowwoc/pouch","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cowwoc%2Fpouch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cowwoc%2Fpouch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cowwoc%2Fpouch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cowwoc%2Fpouch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cowwoc","download_url":"https://codeload.github.com/cowwoc/pouch/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cowwoc%2Fpouch/sbom","scorecard":{"id":306320,"data":{"date":"2025-08-11","repo":{"name":"github.com/cowwoc/pouch","commit":"f034e664967074044bc9da686e054064112352af"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.1,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","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":"Maintained","score":10,"reason":"18 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 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":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: jobLevel 'contents' permission set to 'read': .github/workflows/build-on-push.yml:13","Info: jobLevel 'actions' permission set to 'read': .github/workflows/build-on-push.yml:14","Warn: jobLevel 'checks' permission set to 'write': .github/workflows/build-on-push.yml:15","Info: jobLevel 'contents' permission set to 'read': .github/workflows/deploy_to_maven_central.yml:74","Info: jobLevel 'actions' permission set to 'read': .github/workflows/deploy_to_maven_central.yml:75","Warn: jobLevel 'checks' permission set to 'write': .github/workflows/deploy_to_maven_central.yml:76","Warn: jobLevel 'packages' permission set to 'write': .github/workflows/deploy_to_maven_central.yml:77","Warn: no topLevel permission defined: .github/workflows/build-on-push.yml:1","Warn: topLevel 'contents' permission set to 'write': .github/workflows/cla.yml:11","Warn: topLevel 'statuses' permission set to 'write': .github/workflows/cla.yml:13","Warn: topLevel 'actions' permission set to 'write': .github/workflows/cla.yml:10","Warn: topLevel 'contents' permission set to 'write': .github/workflows/deploy_to_maven_central.yml:7","Warn: no topLevel permission defined: .github/workflows/reusable-build.yml:1"],"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":"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":"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":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"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":"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":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"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 'main'"],"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":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Info: Possibly incomplete results: error parsing shell code: a command can only contain words and redirects; encountered ): .github/workflows/deploy_to_maven_central.yml:277","Warn: third-party GitHubAction not pinned by hash: .github/workflows/cla.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/cowwoc/pouch/cla.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/deploy_to_maven_central.yml:262: update your workflow using https://app.stepsecurity.io/secureworkflow/cowwoc/pouch/deploy_to_maven_central.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/deploy_to_maven_central.yml:267: update your workflow using https://app.stepsecurity.io/secureworkflow/cowwoc/pouch/deploy_to_maven_central.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/deploy_to_maven_central.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/cowwoc/pouch/deploy_to_maven_central.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/deploy_to_maven_central.yml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/cowwoc/pouch/deploy_to_maven_central.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/deploy_to_maven_central.yml:33: update your workflow using https://app.stepsecurity.io/secureworkflow/cowwoc/pouch/deploy_to_maven_central.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/deploy_to_maven_central.yml:89: update your workflow using https://app.stepsecurity.io/secureworkflow/cowwoc/pouch/deploy_to_maven_central.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/deploy_to_maven_central.yml:99: update your workflow using https://app.stepsecurity.io/secureworkflow/cowwoc/pouch/deploy_to_maven_central.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/reusable-build.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/cowwoc/pouch/reusable-build.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/reusable-build.yml:26: update your workflow using https://app.stepsecurity.io/secureworkflow/cowwoc/pouch/reusable-build.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/reusable-build.yml:34: update your workflow using https://app.stepsecurity.io/secureworkflow/cowwoc/pouch/reusable-build.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/reusable-build.yml:68: update your workflow using https://app.stepsecurity.io/secureworkflow/cowwoc/pouch/reusable-build.yml/main?enable=pin","Info:   0 out of  11 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned"],"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":"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"}}]},"last_synced_at":"2025-08-17T22:08:39.086Z","repository_id":44944769,"created_at":"2025-08-17T22:08:39.086Z","updated_at":"2025-08-17T22:08:39.086Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275038261,"owners_count":25394640,"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","status":"online","status_checked_at":"2025-09-13T02:00:10.085Z","response_time":70,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["dependency-injection","inversion-of-control","java","service-locator","variable-scope"],"created_at":"2025-09-13T22:57:13.427Z","updated_at":"2025-09-13T22:57:24.610Z","avatar_url":"https://github.com/cowwoc.png","language":"Java","readme":"[![Maven Central](https://maven-badges.herokuapp.com/sonatype-central/io.github.cowwoc.pouch/pouch-core/badge.svg)](https://central.sonatype.com/search?q=g:io.github.cowwoc.pouch) [![API](https://img.shields.io/badge/api_docs-5B45D5.svg)](http://cowwoc.github.io/pouch/9.1/) [![Changelog](https://img.shields.io/badge/changelog-A345D5.svg)](docs/changelog.md)\n[![build-status](../../workflows/Build/badge.svg)](../../actions?query=workflow%3Abuild)\n\n# \u003cimg alt=\"pouch\" src=\"docs/logo.svg\" width=\"128\" height=\"146\"/\u003e Pouch: Inversion of Control for the Masses\n\nAn [Inversion of Control](http://martinfowler.com/articles/injection.html) design pattern that is:\n\n* Easy to use (no magic!)\n  * No config files\n  * No reflection\n  * No bytecode generation\n  * No proxies\n  * No annotations\n* Easy to debug (dependency graph is verified at compile-time)\n* Dependency-free\n\nTo get started, add this Maven dependency:\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003eio.github.cowwoc.pouch\u003c/groupId\u003e\n  \u003cartifactId\u003epouch-core\u003c/artifactId\u003e\n  \u003cversion\u003e9.1\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n# Getting Started\n\n## Scopes\n\n[Service Locators](docs/frequently_asked_questions.md#not-your-mothers-service-locator) are registries\nthat contain one or more *values*.\nValues can be bound to one or more *scopes*.\nA scope is the context within which a value is defined.\nScopes guarantee that values will remain unchanged during their lifetime.\n\n## Example\n\nPouch doesn't advocate specific designs. You're expected to use whatever design best suits your use-case.\nHere is one design that has worked for me:\n\n### Scope Types\n\n```java\npublic enum RunMode\n{\n  DEBUG,\n  RELEASE\n}\n```\n\n```java\nimport io.github.cowwoc.pouch.core.Scope;\n\npublic interface JvmScope extends Scope\n{\n  RunMode getRunMode();\n}\n```\n\n```java\nimport javax.sql.DataSource;\n\npublic interface DatabaseScope extends JvmScope\n{\n  DataSource getDataSource();\n}\n```\n\n```java\nimport javax.sql.DataSource;\n\npublic interface TransactionScope extends DatabaseScope\n{\n  Connection getConnection();\n}\n```\n\n```java\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\npublic interface ServerScope extends DatabaseScope\n{\n  HttpScope createRequest(HttpServletRequest request, HttpServletResponse response);\n}\n```\n\n```java\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\npublic interface RequestScope extends ServerScope\n{\n  HttpServletRequest getRequest();\n\n  HttpServletResponse getResponse();\n}\n```\n\n```java\nimport org.eclipse.jetty.client.HttpClient;\n\nimport java.net.URI;\n\npublic interface ClientScope extends Scope\n{\n  URI getServer();\n\n  HttpClient getHttpClient();\n}\n```\n\n### Scope Inheritance\n\nImagine we have:\n\n* `JvmScope`: values and variables specific to the lifetime of the current JVM.\n* `DatabaseScope`: values and variables specific to the lifetime of the current database connection.\n* `RequestScope`: values and varibles specific to the lifetime of the current HTTP request.\n\nNotice that a `JvmScope` contains values whose lifetime span multiple database connections.\nFurther, `DatabaseScope` contains values whose lifetime span multiple HTTP requests.\nIn light of this, we define `RequestScope` as extending `DatabaseScope` and `DatabaseScope` as extending\n`JvmScope`.\nWhen a new `RequestScope` is constructed, we pass in the parent scope.\n\nThe lifetime of child scopes must be equal to or less than the lifetime of the parent scope.\nThis means that child scopes can't exist without the parent.\nFor example, the above conceptual model assumes that\nan `RequestScope` cannot exist without a `DatabaseScope` but a `DatabaseScope` may exist without an `RequestScope`.\nThis enables a worker thread to interact with the database outside an HTTP request.\nBut an HTTP request can't exist without a database connection.\n\nWhen a child scope is asked for a value that is bound to its parent, it delegates to the parent.\nFor example, notice how `AbstractDatabaseScope.getRunMode()` delegates to `JvmScope.getRunMode()` below.\n\n### Waiting for Child Scopes to Shut Down\n\nWhen running in a multithreaded environment, such as a web server, you might want to wait for ongoing HTTP\nrequests to complete before shutting down the server.\nYou can use the\n[ConcurrentChildScopes](https://cowwoc.github.io/pouch/9.1/io.github.cowwoc.pouch.core/com/github/cowwoc/pouch/core/ConcurrentChildScopes.html)\nclass to implement this as follows:\n\n```java\nimport io.github.cowwoc.pouch.core.ConcurrentChildScopes;\nimport io.github.cowwoc.pouch.core.Scopes;\nimport io.github.cowwoc.pouch.core.ConcurrentChildScopes;\n\npublic abstract class AbstractJvmScope extends AbstractScope\n    implements JvmScope\n{\n  private final RunMode runMode;\n  private final Duration closeTimeout;\n\n  protected AbstractJvmScope(RunMode runMode)\n  {\n    this.runMode = runMode;\n    this.closeTimeout = switch (runMode)\n    {\n      case DEBUG -\u003e Duration.ofMinutes(10);\n      case RELEASE -\u003e Duration.ofSeconds(30);\n    };\n  }\n  \n  @Override\n  public void close()\n  {\n    Scopes.runAll(() -\u003e children.shutdown(closeTimeout));\n  }\n}\n```\n\n### Implementing DatabaseScope\n\n```java\nimport javax.sql.DataSource;\n\npublic abstract class AbstractDatabaseScope extends AbstractJvmScope\n  implements DatabaseScope\n{\n  private final JvmScope parent;\n\n  protected AbstractDatabaseScope(JvmScope parent)\n  {\n    this.parent = parent;\n  }\n\n  public RunMode getRunMode()\n  {\n    return parent.getRunMode();\n  }\n\n  @Override\n  public void close()\n  {\n  }\n}\n```\n\n```java\nimport javax.sql.DataSource;\n\npublic final class DefaultTransactionScope implements TransactionScope\n{\n  private final DatabaseScope parent;\n\n  public DefaultTransactionScope(DatabaseScope parent)\n  {\n    this.parent = parent;\n  }\n\n  public RunMode getRunMode()\n  {\n    return parent.getRunMode();\n  }\n\n  public DataSource getDataSource()\n  {\n    return parent.getDataSource();\n  }\n\n  @Override\n  public void close()\n  {\n  }\n}\n```\n\n```java\npublic final class MainDatabaseScope extends AbstractDatabaseScope\n{\n  private final Factory\u003c? extends DataSource\u003e dataSource;\n\n  public MainDatabaseScope(JvmScope parent)\n  {\n    super(parent);\n    this.dataSource = new MainDataSourceFactory(this, configuration);\n    parent.addChild(this)\n  }\n\n  @Override\n  public DataSource getDataSource()\n  {\n    return dataSource.getValue();\n  }\n\n  @Override\n  public void close()\n  {\n    dataSource.close();\n    super.close();\n    parent.removeChild(this);\n  }\n}\n```\n\n### Unit tests\n\nWhat's interesting about this design is that you can easily isolate unit tests from each other,\nas if they were running in separate JVMs.\n\n```java\npublic class UnitTest\n{\n  @Test\n  public first()\n  {\n    try (ServerScope scope = new TestServerScope())\n    {\n      Datasource ds = scope.getDataSource();\n      // ...\n    }\n  }\n\n  @Test\n  public second()\n  {\n    try (ServerScope scope = new TestServerScope())\n    {\n      Datasource ds = scope.getDataSource();\n      // ...\n    }\n  }\n}\n```\n\nYou can even configure each test to run against a separate database:\n\n```java\npublic final class TestDatabaseScope extends AbstractDatabaseScope\n{\n  private final Factory\u003c? extends DataSource\u003e dataSource;\n\n  public TestDatabaseScope(JvmScope parent, Configuration configuration, int id)\n  {\n    super(parent, configuration);\n    dataSource = new TestDataSourceFactory(this, configuration, id);\n  }\n\n  @Override\n  public DataSource getDataSource()\n  {\n    return dataSource.getValue();\n  }\n\n  @Override\n  public void close()\n  {\n    dataSource.close();\n    super.close();\n  }\n}\n```\n\n```java\nimport javax.sql.DataSource;\n\npublic final class MainDataSourceFactory extends ConcurrentLazyFactory\u003cDataSource\u003e\n{\n  private final DatabaseScope scope;\n\n  public MainDataSourceFactory(DatabaseScope scope)\n  {\n    this.scope = scope;\n  }\n\n  @Override\n  protected DataSource createValue()\n  {\n    // Return a DataSource pointing to the main database\n  }\n\n  @Override\n  protected void disposeValue(DataSource dataSource)\n  {\n    dataSource.close();\n  }\n}\n```\n\n```java\nimport io.github.cowwoc.pouch.core.ConcurrentLazyFactory;\n\nimport java.net.URI;\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\n\npublic final class TestDataSourceFactory extends ConcurrentLazyFactory\u003cDataSource\u003e\n{\n  private final DatabaseScope scope;\n  private final String databaseName;\n\n  public TestDataSourceFactory(DatabaseScope scope, int id)\n  {\n    this.scope = scope;\n    this.databaseName = \"test-\" + id;\n  }\n\n  private Connection getConnection()\n  {\n    // Return connection to the database\n  }\n\n  @Override\n  protected DataSource createValue()\n  {\n    // Create a test-specific database\n    try (Connection connection = getConnection())\n    {\n      connection.setAutoCommit(true);\n      try (Statement statement = connection.createStatement())\n      {\n        statement.executeUpdate(\"DROP DATABASE IF EXISTS \" + databaseName);\n        statement.executeUpdate(\"CREATE DATABASE \" + databaseName);\n      }\n    }\n    catch (SQLException e)\n    {\n      throw new RuntimeException(\"\", e);\n    }\n    // Return a DataSource pointing to the test-specific database\n  }\n\n  @Override\n  protected void disposeValue(DataSource dataSource)\n  {\n    try (Connection connection = dataSource.getConnection())\n    {\n      try (Statement statement = connection.createStatement())\n      {\n        statement.executeUpdate(\"DROP DATABASE IF EXISTS \" + configuration.databaseName());\n      }\n    }\n    catch (SQLException e)\n    {\n      throw new DataAccessException(\"\", e);\n    }\n    dataSource.close();\n  }\n}\n```\n\n### Scopes that Return Different Values Over Their Lifetime\n\nScopes that need to return different values over their lifetime can return\na [Builder](https://en.wikipedia.org/docs/Builder_pattern)\nor [Supplier](https://docs.oracle.com/javase/8/java/util/function/Supplier.html) that will, in turn,\nreturn different values on every invocation.\n\nHere is a contrived example:\n\n```java\npublic interface ClientScope\n{\n  Supplier\u003cLocalDateTime\u003e getServerTime();\n}\n\n  public Main\n  {\n    public static void main (String[]args)\n    {\n      try (ClientScope scope = new MainClientScope())\n      {\n        Supplier\u003cLocalTime\u003e serverTime = scope.getServerTime();\n        for (int i = 0; i \u003c 10; ++i)\n          System.out.println(\"Server time: \" + serverTime.get());\n      }\n    }\n  }\n```\n\n### How do I break circular dependencies?\n\nThe easiest way to break circular dependencies is using lazy initialization.\n\nDependency injection frameworks recommend injecting `Provider\u003cFoo\u003e` in place of `Foo`,\nwhere the former constructs `Foo` lazily.\n\n````java\nclass DogTrainer\n{\n  private final Cat cat;\n  private final Dog dog;\n\n  public DogTrainer(Cat cat, Dog dog)\n  {\n    this.cat = cat;\n    this.dog = dog;\n  }\n\n  void meow()\n  {\n    cat.meow();\n  }\n\n  void bark()\n  {\n    dog.bark();\n  }\n}\n````\n\nbecomes:\n\n````java\nclass DogTrainer\n{\n  private final Provider\u003cCat\u003e catProvider;\n  private final Provider\u003cDog\u003e dogProvider;\n\n  public DogTrainer(Provider\u003cCat\u003e catProvider, Provider\u003cDog\u003e dogProvider)\n  {\n    this.catProvider = catProvider;\n    this.dogProvider = dogProvider;\n  }\n\n  void meow()\n  {\n    Cat cat = catProvider.get();\n    cat.meow();\n  }\n\n  void bark()\n  {\n    Dog dog = dogProvider.get();\n    dog.bark();\n  }\n}\n````\n\nPouch recommends using `Reference\u003cFoo\u003e` in place of `Foo`, where the former constructs `Foo` lazily.\nAlternatively, you could replace `Foo` by a scope that constructs it lazily.\n\n````java\nclass DogTrainer\n{\n  private final Cat cat;\n  private final Dog dog;\n\n  public DogTrainer(Cat cat, Dog dog)\n  {\n    this.cat = cat;\n    this.dog = dog;\n  }\n\n  void meow()\n  {\n    cat.meow();\n  }\n\n  void bark()\n  {\n    dog.bark();\n  }\n}\n````\n\nbecomes:\n\n````java\nclass AnimalPen\n{\n  private final Reference\u003cCat\u003e catReference;\n  private final Reference\u003cDog\u003e dogReference;\n\n  public AnimalPen(Reference\u003cCat\u003e catReference, Reference\u003cDog\u003e dogReference)\n  {\n    this.catReference = catReference;\n    this.dogReference = dogReference;\n  }\n\n  void meow()\n  {\n    Cat cat = catReference.get();\n    cat.meow();\n  }\n\n  void bark()\n  {\n    Dog dog = dogReference.get();\n    dog.bark();\n  }\n}\n````\n\nor\n\n````java\nclass DogTrainer\n{\n  private final JvmScope scope;\n\n  public DogTrainer(JvmScope scope)\n  {\n    this.scope = scope;\n  }\n\n  void meow()\n  {\n    Cat cat = scope.getCat();\n    cat.meow();\n  }\n\n  void bark()\n  {\n    Dog dog = scope.getDog();\n    dog.bark();\n  }\n}\n````\n\nThe scope approach makes it easier to look up multiple values, or pass the scope to another object.\n\n### What's the difference between LazyFactory and ConcurrentLazyFactory?\n\nThe library contains two types of classes: ones that are thread-safe and ones that are not.\n\nFor\nexample, [ConcurrentLazyFactory](https://cowwoc.github.io/pouch/9.1/io.github.cowwoc.pouch.core/com/github/cowwoc/pouch/core/ConcurrentLazyFactory.html)\nis the thread-safe equivalent\nof [LazyFactory](https://cowwoc.github.io/pouch/9.1/io.github.cowwoc.pouch.core/com/github/cowwoc/pouch/core/LazyFactory.html).\n`LazyFactory` is faster than `ConcurrentLazyFactory`, but doesn't support access from multiple threads.\nClasses that are required to support multithreaded access (such as the JVM scope) must use the\nthread-safe classes.\n\n## Try it!\n\nThe [jersey plugin](jersey/src) contains a working example. Download a copy and try it for yourself.\n\n# Class guide\n\n![class-guide.png](docs/class-guide.png)\n\n# Plugin modules\n\nThe following sample code demonstrates integration with various third-party libraries:\n\n* [Jersey](jersey/): Integrates pouch with Jersey.\n* [Dropwizard](dropwizard/): Integrates pouch with Dropwizard.\n\n# Related projects\n\n* [Requirements](https://github.com/cowwoc/requirements.java/): Fluent Design by Contract for Java APIs.\n* [JayWire](https://github.com/vanillasource/jaywire): the power of dependency injection\n  without the \"magic\".\n\n# License\n\n* This library is distributed under the terms of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0)\n* The logo icon was licensed from [Flat Icons](https://www.flaticon.com/) under the terms of the [CC 3.0 BY](https://creativecommons.org/licenses/by/3.0/) license.","funding_links":["https://github.com/sponsors/cowwoc"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcowwoc%2Fpouch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcowwoc%2Fpouch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcowwoc%2Fpouch/lists"}