{"id":22072944,"url":"https://github.com/unruly/java-config-fallback","last_synced_at":"2026-04-02T02:53:28.078Z","repository":{"id":57716337,"uuid":"128050378","full_name":"unruly/java-config-fallback","owner":"unruly","description":"A lightweight library to waterfall configuration lookup from multiple sources.","archived":false,"fork":false,"pushed_at":"2018-09-21T17:04:32.000Z","size":53,"stargazers_count":6,"open_issues_count":1,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-15T18:26:08.845Z","etag":null,"topics":["configuration","java"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/unruly.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-04-04T11:05:08.000Z","updated_at":"2021-04-04T17:54:18.000Z","dependencies_parsed_at":"2022-08-25T11:01:02.361Z","dependency_job_id":null,"html_url":"https://github.com/unruly/java-config-fallback","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/unruly/java-config-fallback","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unruly%2Fjava-config-fallback","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unruly%2Fjava-config-fallback/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unruly%2Fjava-config-fallback/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unruly%2Fjava-config-fallback/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/unruly","download_url":"https://codeload.github.com/unruly/java-config-fallback/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unruly%2Fjava-config-fallback/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265691990,"owners_count":23812180,"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":["configuration","java"],"created_at":"2024-11-30T21:16:07.287Z","updated_at":"2026-04-02T02:53:28.039Z","avatar_url":"https://github.com/unruly.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# java-config-fallback\n\n[![Build Status](https://travis-ci.org/unruly/java-config-fallback.svg?branch=master)](https://travis-ci.org/unruly/java-config-fallback)\n[![Release Version](https://img.shields.io/maven-central/v/co.unruly/java-config-fallback.svg)](https://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22co.unruly%22%20AND%20a%3A%22java-config-fallback%22)\n\nA lightweight library to query a hierarchy of configuration sources\n\n```java\nConfiguration config = Configuration.of(\n    secretsManager(\"my-secret-name\", \"eu-west-1\"), // { \"username\": \"user\", \"password\": \"secret\" }\n    properties(\"/etc/my-app-config.properties\"),   // { \"username\": \"anotherUser\" }\n);\n\nOptional\u003cString\u003e username = config.get(\"username\"); // Optional[user]\nOptional\u003cString\u003e password = config.get(\"password\"); // Optional[secret] \n```\n\n## Contents\n\n- [Install](#install)\n- [Usage](#usage)\n  - [Fallback Behaviour](#fallback-behaviour)\n- [Supported Configuration Sources](#supported-configuration-sources)\n  - [Map](#map)\n  - [Properties](#properties)\n  - [System Properties](#system-properties)\n  - [Environment](#environment)\n  - [AWS Secrets Manager](#aws-secrets-manager)\n- [Extending with custom Configuration Sources](#extending-with-custom-configuration-sources)\n- [Contributing](#contributing)\n  - [Code of Conduct](#code-of-conduct)\n  - [Getting Started](#getting-started)\n  - [Releasing a Change](#releasing-a-change)\n- [License](#license)\n- [Design Decisions](#design-decisions)\n\n## Install\n\nThis library is available on Maven Central\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eco.unruly\u003c/groupId\u003e\n    \u003cartifactId\u003ejava-config-fallback\u003c/artifactId\u003e\n    \u003cversion\u003e1.0.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n## Usage\n\n### Fallback Behaviour\n\nWhen queried for a key, a `Configuration` instance will query each `ConfigurationSource` passed to its builder **in order** until it finds a key.\n\nExample:\n\n```java\nConfiguration config = Configuration.of(\n    properties(\"high-priority-config.properties\"),\n    properties(\"default.properties\"),\n    environment()\n);\n```\n\n* This configuration will first look for a key in `high-priority-config.properties`.\n* If it is not there, it will then look for that key in `default.properties`\n* If it is not in either of those, then it will check the application's environment variables.\n\nBehaviour if a key cannot be retrieved from any of the provided sources (whether from missing key or missing file or network timeout) depends on the method used to query.\n\n| Method       | Parameters                   | Success Behaviour                      | Failure Behaviour                              |\n|:-------------|:-----------------------------|:---------------------------------------|:-----------------------------------------------|\n| `.get()`     | `String key`                 | Returns `Optional\u003cString\u003e.of(\u003cvalue\u003e)` | Returns `Optional\u003cString\u003e.empty()`             |\n| `.get()`     | `String key, String default` | Returns `Optional\u003cString\u003e.of(\u003cvalue\u003e)` | Returns `Optional\u003cString\u003e.of(\u003cdefault-value\u003e)` |\n| `.require()` | `String key`                 | Returns `\u003cvalue\u003e` as a String          | Throws `ConfigurationMissing` exception        |\n\n## Supported Configuration Sources\n\n### Map\n\nThe `.map()` configuration source takes a `Map\u003cString, String\u003e` as a datasource.\n\n```java\nMap\u003cString, String\u003e credentials = new HashMap\u003c\u003e();\ncredentials.put(\"username\",\"foo\");\ncredentials.put(\"password\",\"this-is-a-secret!\");\n\nConfiguration config = Configuration.of(map(credentials));\n\nOptional\u003cString\u003e username = config.get(\"username\"); // Optional[foo]\nOptional\u003cString\u003e password = config.get(\"password\"); // Optional[this-is-a-secret!]\n```\n\n### Properties\n\nThe `.properties()` configuration source takes a `String filePath` pointing to a `.properties` file as a datasource.\n\n```java\nConfiguration config = Configuration.of(\n    properties(\"/etc/my-app/config.properties\"),  // password=new-password\n);\n\nOptional\u003cString\u003e password = config.get(\"password\"); // Optional[new-password]\n```\n\n### System Properties\n\nThe `.systemProperties()` configuration source uses `System.getProperty()` as a datasource.\n\n```java\nConfiguration config = Configuration.of(\n    systemProperties()\n);\n\n// Running code with `mvn clean test -Dmy.property=hello-world\nOptional\u003cString\u003e myProperty = config.get(\"my.property\"); // Optional[hello-world]\n\n// Default system properties in every JVM\nOptional\u003cString\u003e javaVersion = config.get(\"java.version\"); // Optional[1.8.0_12345]\n```\n\n### Environment\n\nThe `.environment()` configuration source uses environment variables set for this application.\n\n**NOTE**: This assumes environment variables are UPPER_CASE and converts queried keys to UPPER_CASE before lookup.\n\n```java\n// With VARIABLE=foo set\n\nConfiguration config = Configuration.of(environment());\n\nOptional\u003cString\u003e password = config.get(\"VARIABLE\"); // Optional[foo]\nOptional\u003cString\u003e password = config.get(\"variable\"); // Same as above\n```\n\n### AWS Secrets Manager\n\nThe `.secretsManager()` configuration source uses AWS Secrets Manager as its datasource.\n\nThis can be configured with:\n\n * `.secretsManager(String secretName, String region)` - uses default AWS client to query Secrets Manager.\n * `.secretsManager(String secretName, String region, AWSSecretsManager client)` - uses provided client to query Secrets Manager. Use this if you want to provide custom client behaviour e.g. using a specific set of credentials or instance-role.\n \nSecrets are only requested from AWS the first time they're accessed. Subsequent\nrequests using the same configuration will use a cached response.\n\n**NOTE**: This configuration source only supports key-value pairs as a return type - plaintext secrets will be ignored.\n\n```java\nConfiguration config = Configuration.of(\n    secretsManager(\"my-secret\", \"eu-west-1\") // { \"username\": \"user\", \"password\": \"secret\" }\n); \n\nOptional\u003cString\u003e password = config.get(\"username\"); // Optional[user]\nOptional\u003cString\u003e password = config.get(\"password\"); // Optional[secret]\n```\n\n## Adding custom Configuration Sources\n\nYou can define your own sources of configuration by implementing the `ConfigurationSource` functional interface.\n\nThe contract between this and the `Configuration` class is:\n * A `ConfigurationSource` will not throw exceptions when queried.\n * A `ConfigurationSource` will return `null` if the requested key cannot be found or other error occurs.\n * A `ConfigurationSource` will return a `String` representing the value of a successful lookup.\n\nFor example, a custom `ConfigurationSource` that returns a different random string every time it's queried:\n\n```java\nimport java.util.UUID;\n\npublic class RandomUUIDSource implements ConfigurationSource {\n    @Override\n    public String get(String key) {\n        return UUID.randomUUID().toString();\n    }\n}\n```\n\n**NOTE**: Don't actually do this.\n\n## Contributing\n\n### Code of Conduct\n\nEveryone interacting with this project is required to follow the [Code of Conduct](./CODE_OF_CONDUCT.md).\n\n### Getting Started\n\nYou'll need Maven and Java 8+ installed\n\n```bash\n$ git clone git@github.com:unruly/java-config-fallback.git\n$ mvn clean install\n```\n\nYou can run `mvn clean test` in the project root to run JUnit tests.\n\n### Releasing a Change\n\n- To release a new version:\n  - Run `mvn release:prepare release:perform` - NOTE: this requires signing the release with a GPG key.\n\n## Design Decisions\n\nThere are already libraries (such as [properlty](https://github.com/ufoscout/properlty)) that do something very similar — why build our own?\n\n * **Focus on cleanliness and simplicity**: We aim to keep the core library at fewer than 100 lines of code.\n \n * **Architecting for extensibility**: the `ConfigurationSource` entry point is used to retrieve a specific value for a key, not to load all possible values into memory. This opens the door to providers for external services that operate on a request-by-request basis.\n \n * **Use modern Java features**: we designed `ConfigurationSource` as a functional interface, so custom providers can be defined in-line as lambda expressions:\n```java\nConfiguration config = Configuration.of(\n  (key) -\u003e \"always-this-value\"\n);\n```\n\n * **Short-circuit querying**: Incorporating `.orElseGet(() -\u003e ...)` into the composition of the sources stops the query being evaluated if a value is found “higher up”. For services where the call is non-trivial or expensive, this is much more efficient.\n \n * **Do one thing well**: The library doesn’t coerce types (opting for String key, String value) or assume behaviour if a source cannot be queried (missing file or slow network call). This is up to the user to decide, and simplifies a lot of our other design choices.\n \n * **Eat our own dog-food**: This library is in use at Unruly, helping us transition our app configuration and advance our architecture. We don’t want to release anything we wouldn’t use ourselves.\n\n## License\n \nThis project is available as open source under the terms of the [MIT License](./LICENSE).\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funruly%2Fjava-config-fallback","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Funruly%2Fjava-config-fallback","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funruly%2Fjava-config-fallback/lists"}