{"id":22989011,"url":"https://github.com/ggranum/cli-annotations","last_synced_at":"2025-08-02T20:04:57.612Z","repository":{"id":5336777,"uuid":"6522261","full_name":"ggranum/cli-annotations","owner":"ggranum","description":"Parse your command line arguments straight into an annotated POJO. Annotations are library agnostic; currently support is included for JOptSimple.","archived":false,"fork":false,"pushed_at":"2016-08-11T11:15:19.000Z","size":89,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-24T17:02:17.094Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/ggranum.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":"2012-11-03T17:45:54.000Z","updated_at":"2019-11-14T00:05:00.000Z","dependencies_parsed_at":"2022-07-06T00:00:37.556Z","dependency_job_id":null,"html_url":"https://github.com/ggranum/cli-annotations","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ggranum/cli-annotations","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ggranum%2Fcli-annotations","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ggranum%2Fcli-annotations/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ggranum%2Fcli-annotations/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ggranum%2Fcli-annotations/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ggranum","download_url":"https://codeload.github.com/ggranum/cli-annotations/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ggranum%2Fcli-annotations/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268448176,"owners_count":24251994,"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-08-02T02:00:12.353Z","response_time":74,"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":[],"created_at":"2024-12-15T04:15:44.198Z","updated_at":"2025-08-02T20:04:57.564Z","avatar_url":"https://github.com/ggranum.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"## cli-annotations\n=======================\n\nParsing input is so much fun. How else to explain why we've all taken the time to roll our own implementations of\nboth command line and property file based configuration readers?\n\nOr not. How about we switch to something more comfortable: annotate a POJO and read your configuration into it with\nthree lines of extra code?\n\n#### Easier, more familiar configuration.\nCLI Annotations is a misnomer. The project started as \"just\" an extension of the excellent JOptSimple. And it was good.\nParsing the command line straight into an annotated POJO was pretty slick, all by itself.\n\nBut, eventually, all applications evolves to \u003cdel\u003esend mail\u003c/del\u003e become a web-app. Which means\nconfiguring from a resource property file, or perhaps just a property file in the startup directory. So\ncli-annotations has evolved to read properties files. With just one more annotation and a few lines of code you can\nswitch from reading configuration off the command line to reading configuration from one or more properties files.\nAnd the extra annotation isn't even required - of course, your properties file will use some admittedly strange keys\nif you accept the default.\n\nThat's really slick.\n\n#### Some examples\n\n##### A configuration POJO\n\nAs indicated by the class name, this class was borrowed from the unit tests. It's extra verbose,\nfilling in many extra options for your viewing pleasure:\n\n    public class ProcessorTestConfig {\n\n        @CliOption(\n                shortOption = \"b\",\n                longOption = \"booleanLongOption\",\n                propertyKey = \"booleanPropertyKey\",\n                description = \"A single boolean value\"\n        )\n        public boolean booleanValue;\n\n        @CliOption(\n                shortOption = \"i\",\n                longOption = \"integerLongOption\",\n                propertyKey = \"integerPropertyKey\",\n                description = \"Simplest integer value.\"\n        )\n        public int integerValue;\n\n        @CliOption(\n                shortOption = \"j\",\n                longOption = \"integerListLongOption\",\n                propertyKey = \"integerListPropertyKey\",\n                description = \"List of integer values.\",\n                argument = @CliOptionArgument()\n        )\n        public List\u003cInteger\u003e integerList;\n\n        @CliOption(\n                shortOption = \"k\",\n                longOption = \"integerListDefaultsLongOption\",\n                propertyKey = \"integerListDefaultsPropertyKey\",\n                description = \"List of integer values with defaults.\",\n                argument = @CliOptionArgument(defaultValue = {\"10\", \"20\", \"30\"})\n        )\n        public List\u003cInteger\u003e integerListWithDefaults;\n    }\n\n\n##### Reading ProcessorTestConfig from a Properties File (e.g. $projectDir/src/main/resources/configuration.properties)\n\n    public void SomeConfigurationMethod(){\n        PropertyReaderModelProcessor\u003cProcessorTestConfig\u003e processor =\n                    new PropertyReaderModelProcessor\u003cProcessorTestConfig\u003e(ProcessorTestConfig.class);\n            Properties p = processor.addResourcePath(\"/configuration.properties\", getClass());\n            // if p is null, your resource path was wrong!\n            ProcessorTestConfig config = processor.processInput();\n            // do something with config.\n    }\n\nThat's all you have to do to parse from a single properties file. Configuration.properties looks something like this:\n\n    ~configuration.properties\n        booleanPropertyKey=true\n        integerPropertyKey=100\n        integerListPropertyKey=30,40,50\n        floatPropertyKey=10.10\n        stringValuePropertyKey=foo'tacular\n\n\nThat's awesome.\n\nBut there's even more awesome. How about chained properties files?\n\n##### Reading a configuration from multiple properties files\n\nSo here's the use case: Your local configuration is different from the configuration you ship with your product. It's\n a bit of an edge case, but hey, what are 3rd party libs for, if not to help with your esoteric workflows?\n\nLet's say you have two configuration files:\n\n    $projectDir/src/main/resources/configuration.properties\n    $projectDir/src/main/resources/configuration.local.properties\n\nYour .gitIgnore file keeps the .local.properties file from ever being committed; it exists only to override your\nproduction configuration values.\n\nWe'll use the above listing for configuration.properties, and we'll use our local version to override a couple values:\n\n    ~configuration.local.properties\n        booleanPropertyKey=false\n        integerPropertyKey=9999\n        stringValuePropertyKey=qux'tacular\n\nAnd we'll update our config method to read them into one configuration object:\n\n    public void SomeConfigurationMethod(){\n        PropertyReaderModelProcessor\u003cProcessorTestConfig\u003e processor =\n                    new PropertyReaderModelProcessor\u003cProcessorTestConfig\u003e(ProcessorTestConfig.class);\n            Properties p = processor.addResourcePath(\"/configuration.properties\", getClass());\n            // if p is null, your resource path was wrong! p only holds values from configuration.properties\n            p = processor.addResourcePath(\"/configuration.local.properties\", getClass());\n            // if p is null, your resource path was wrong! p only holds values from configuration.local.properties\n            // Use this feature to help debug your environment, if something seems awry.\n            ProcessorTestConfig config = processor.processInput();\n            // config is fully populated, with configuration.local.properties overriding values from\n            // configuration.properties\n    }\n\n\n\"But wait!\" You cry. \"We don't want to commit configuration.local.properties, or use it in production!\"\n\nOf course not. #addResourcePath (and the #addInputSource(InputStream) version) ignore null values. So you are\n free to add as many 'overrides' as you want - just make sure they don't make it into your builds!\n\nThere's no limit to the number of paths you can throw at it, and, as mentioned, there is also an \"#addInputSource\n(InputStream)\" version of the #addResourcePath method so that you can point to the file system as well.\n\n\n#### Reading from the command line\n\nThe command line is just as easy, and thanks to JOptSimple it will even generate most of your help listing for you.\nSo long as you use reasonable option names (in the annotations) you won't need to augment the help output by much,\neven for user facing applications. Let's set up a main method for the same configuration POJO as we showed above,\nwith one difference - make ProcessorTestConfig extend CliConfig:\n\n    public class ProcessorTestConfig extendsd CliConfig {\n        // ...  body as before\n    }\n\nAnd now parse:\n\n    public static void main(String[] args) throws Exception{\n        JoptSimpleCliModelProcessor\u003cProcessorTestConfig\u003e processor =\n                new JoptSimpleCliModelProcessor\u003cProcessorTestConfig\u003e(ProcessorTestConfig.class);\n        ProcessorTestConfig config = processor.processInput(args);\n        if(config.showHelp()) {\n            processor.printHelp(out);\n        }\n        else{\n            startYourProgram(config);\n        }\n    }\n\nIt really doesn't get much easier than that.\n\n\n### Getting Jars\n\nBranch the repo and then execute the gradle build:\n\n    \u003e gradle build\n\nThis will generate three jars; one for cli-annotations and one each for the CLI and PropertyReader plugins.\n\nBoth plugins dependon commons-lang:\n\n    ~build.gradle\n        ...\n        compile 'commons-lang:commons-lang:2.6'\n\nThe CLI plugin needs JOptSimple:\n\n    ~build.gradle\n        ...\n        compile 'net.sf.jopt-simple:jopt-simple:4.3'\n\n\n### Have fun!\n\n\n\n\n#### I'd appreciate feedback, bug reports and/or pull requests.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fggranum%2Fcli-annotations","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fggranum%2Fcli-annotations","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fggranum%2Fcli-annotations/lists"}