{"id":26018520,"url":"https://github.com/peterbecker/configuration","last_synced_at":"2025-07-05T11:35:07.279Z","repository":{"id":26869946,"uuid":"30330347","full_name":"peterbecker/configuration","owner":"peterbecker","description":"Configuration annotations and parser.","archived":false,"fork":false,"pushed_at":"2023-03-11T02:02:47.000Z","size":91,"stargazers_count":4,"open_issues_count":11,"forks_count":0,"subscribers_count":3,"default_branch":"develop","last_synced_at":"2024-11-15T21:46:26.577Z","etag":null,"topics":["configuration","configuration-annotations","configuration-files","java","json","properties","xml","yaml"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/peterbecker.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-02-05T01:05:35.000Z","updated_at":"2022-12-14T21:52:09.000Z","dependencies_parsed_at":"2023-01-14T05:27:09.523Z","dependency_job_id":null,"html_url":"https://github.com/peterbecker/configuration","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterbecker%2Fconfiguration","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterbecker%2Fconfiguration/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterbecker%2Fconfiguration/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/peterbecker%2Fconfiguration/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/peterbecker","download_url":"https://codeload.github.com/peterbecker/configuration/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242157900,"owners_count":20081155,"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","configuration-annotations","configuration-files","java","json","properties","xml","yaml"],"created_at":"2025-03-06T06:21:17.780Z","updated_at":"2025-03-06T06:21:18.362Z","avatar_url":"https://github.com/peterbecker.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Configuration Parsing\n\n[![Build Status](https://travis-ci.org/peterbecker/configuration.svg?branch=master)](https://travis-ci.org/peterbecker/configuration)\n\nA Java configuration library, allowing loading configuration options for command-line tools in a very lightweight manner.\n\nThe core concept of this library is to use interfaces with\nannotated getters to define configuration data of components. These can then be parsed from various data sources.\nAt the moment only property files are supported, the development versions also support XML, JSON and YAML. Support\nfor storing configuration in JDBC databases and for overriding settings on the command line is planned.\n\nThe parser in this library requires Java 8. The code using the interfaces can be in older Java versions.\n\nTo use the configuration parser the following Maven dependency is needed:\n\n```\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.github.peterbecker\u003c/groupId\u003e\n    \u003cartifactId\u003econfiguration-parser\u003c/artifactId\u003e\n    \u003cversion\u003e1.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nIf you want to use annotations for your configuration interfaces you need this dependency. This is implied by the parser\ndependency above, you need the explicit dependency only if you want to use the annotations in a library that does not\ndepend on the parser.\n\n```\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.github.peterbecker\u003c/groupId\u003e\n    \u003cartifactId\u003econfiguration-api\u003c/artifactId\u003e\n    \u003cversion\u003e1.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nThe parser is required only for the modules using it, the annotation should sit where annotations\nare needed.\n\nFor example: your database access layer might depend on the annotation API to allow documenting its configuration\ninterface, whereas the scheduled job that uses the access layer will depend on the parser.\n\nUsage of the API is optional, which means library code can stay completely independent of this library.\n\n# Basic Usage\n\nTo define a configuration, just define an interface using the option names as method names, and the option types as\nreturn types. For example this is a valid configuration interface:\n\n```java\npublic interface MyFirstConfiguration {\n    int anIntegerValue();\n    String aTextValue();\n    Optional\u003cLocalDate\u003e anOptionalDate();\n}\n```\n\nA corresponding properties file would look like this:\n\n```\nanIntegerValue=23\naTextValue=some text\nanOptionalDate=2015-02-19\n```\n\nIn this file the first two items are mandatory, if either of them is missing loading the configuration will fail. The date\ncan be omitted, in which case the return value in the interface will be an empty `Optional`.\n\nTo parse this configuration, the following code can be used:\n\n```java\nPath configFile = Paths.get(\"config.properties\");\nMyFirstConfiguration config =\n                Configuration.\n                        loadInterface(MyFirstConfiguration.class).\n                        fromStore(new PropertiesStore(configFile)).\n                        done();\n```\n\nThe resulting `config` object will have the values set that are given in the properties file.\n\nOne important thing to notice here is that the configuration interface does not use anything beyond the JDK provided\ncapabilities. This means that the configured components do not have any additional dependencies.\n\nThe interfaces also make testing easy: a test class can implement it in any way wanted, including just using an\ninner class.\n\nA number of value types are supported in this. This includes: `String`, all primitive types and their wrappers; all value\ntypes from `java.time.*`; `BigInteger`, and `BigDecimal`. Most of these use standard JDK methods to map them from \nStrings (e.g. `Integer::parseInt`).\n\nAny type but the primitives can be wrapped into an `Optional` to make it optional.\n\n# Other Configuration File Formats\n\nThe standard library supports using Java Properties files and XML as file formats. In the case of XML the configuration\nis expected within an arbitrary root element. An example XML file for the interface above would be:\n\n```xml\n\u003cconfig\u003e\n    \u003canIntegerValue\u003e23\u003c/anIntegerValue\u003e\n    \u003caTextValue\u003esome text\u003c/aTextValue\u003e\n    \u003canOptionalDate\u003e2015-02-19\u003c/anOptionalDate\u003e\n\u003c/config\u003e\n```\n\nTo load this XML, the following can be used:\n\n```java\nPath configFile = Paths.get(\"config.xml\");\nMyFirstConfiguration config =\n                Configuration.\n                        loadInterface(MyFirstConfiguration.class).\n                        fromStore(new XmlStore(configFile)).\n                        done();\n```\n\nJSON and YAML are supported by separate modules, to use either of these format adds extra dependencies with the\n`artifactId` set to `configuration-json` and/or `configuration-yaml`.\n\n\n# Advanced Setup With Annotations\n\nFor advanced\nconfiguration options annotations are used, in which case these become a dependency. The annotations are in a separate\nmodule without runtime dependencies, which means that even in this scenario the footprint is very small.\n\nTwo annotations are available: `@Configuration` and `@Option`. The former is a tagging interface, i.e. it serves no\nfunctionality apart from making it clear to a reader of the code that the interface is intended as configuration. The\n`@Option` has additional functions.\n\nThe first one is that `@Option` offers a `defaultValue` attribute that can be used to\nprovide a string that is used in case no string is provided in the configuration input. The string provided as default\nis processed in the same way a string in an input file would: it will be parsed into the return type specified in the\ninterface.\n\nThis is different to using the\n`Optional` wrapper in that defaulting is invisible to the component using the configuration. If there is a default\nconfigured, the interface will always provide some value. `Optional` allows communicating the absence of a value\nexplicitly, but the code using it will need to handle it.\n\n`@Option` also has a `description` attribute, which can be used to document the function of the option. This is intended\nfor command line usage, but that part is not yet implemented. At the moment it serves a role similar to JavaDoc.\n\nUsing the annotations an interface looks like this:\n\n```java\n@Configuration\npublic interface ConfigInterface {\n     @Option(\n        description = \"This value has to be set\"\n     )\n     int mandatoryValue();\n\n     @Option(\n        description = \"This value will default to 53\",\n        defaultValue = \"53\"\n     )\n     int defaultingValue();\n}\n```\n\n# Nesting Configurations\n\nOne advantage of this library over many other configuration approaches is that the configuration interfaces can be nested.\nFor example a database configuration can be part of an application configuration. The database configuration is defined\nas part of a database layer, there is no need for the database layer to know anything else.\n\nIf a property file is used, nesting is mapped to a dot notation. For example if we have:\n\n```java\npublic interface SocketConfiguration {\n    String hostName();\n    int port();\n}\n```\n\nand\n\n```java\npublic interface ServerConfiguration {\n    SocketConfiguration serverSocket();\n    int numberOfWorkerThreads();\n}\n```\n\nthen a matching configuration file could look like this:\n\n```\nserverSocket.hostName=localhost\nserverSocket.port=1223\nnumberOfWorkerThreads=8\n```\n\nIn input formats that support nesting (XML, YAML, JSON) configurations will appear nested in the files.\n\nCode using this configuration might look like this:\n\n```java\npublic Server {\n    public Server(ServerConfiguration config) {\n        Socket socket = new Socket(config.serverSocket());\n        createWorkerPool(socket, config.numberOfThreads());\n    }\n    ...\n}\n```\n\nHere the `Socket` object is unaware of the the `ServerConfiguration`, all it knowns is the `SocketConfiguration`. Hence\nthe components are cleanly separated.\n\n# Repeated Elements (Lists)\n\nIt is possible to use the standard `java.util.List` interface as a return type. This will allow repeating the element\nin those formats that allow repeated elements (currently XML, JSON, YAML), in the properties format it will expect the\nindex as an additional segment (0-based). For example:\n\n```properties\nstrings.0=First\nstrings.1=Second\nstrings.2=Third\n```\n\nAbove example would be a valid properties file for this interface:\n\n```java\npublic interface MyConfig {\n    List\u003cString\u003e strings();\n}\n```\n\nLists can be used in combination with any values, including nested interfaces.\n\n# Custom Value Parsing\n\nValues are parsed using handlers from Strings to the return type of the getters. Custom parsers can be registered as\nfunctions from `String` to the target type:\n\n```java\nConfigInterface config =\n        Configuration.\n                loadInterface(ConfigInterface.class).\n                fromPropertiesFile(configFile).\n                withValueParser(MyType.class, MyType::parse).\n                done();\n```\n\nThe example uses a static method handle and assumes a static method on `MyType` like this:\n\n```java\npublic static MyType parse(String input) {\n    // parse and return value\n}\n```\n\nExplicit `Function` instances or lambda expressions can be used as alternative to the static method handle above.\n\nOnce a parse function is registered, the value can be used in a configuration interface. This is true for both the\ndirect use, as well as indirect use such as `Optional\u003cMyType\u003e`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpeterbecker%2Fconfiguration","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpeterbecker%2Fconfiguration","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpeterbecker%2Fconfiguration/lists"}