├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── main ├── java │ └── io │ │ └── advantageous │ │ └── config │ │ ├── Config.java │ │ ├── ConfigFromObject.java │ │ ├── ConfigLoader.java │ │ ├── ConfigMemorySize.java │ │ ├── Configs.java │ │ ├── MemorySizeUnit.java │ │ └── ResourceUtils.java └── resources │ └── jjs-config-utils.js └── test ├── java └── io │ └── advantageous │ └── config │ ├── ConfigImplTest.java │ ├── ConfigMemorySizeTest.java │ ├── JsConfigTest.java │ ├── JsLoadTest.java │ └── MemorySizeUnitTest.java └── resources ├── bad-config.js ├── reference.js └── test-config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific stuff: 2 | .idea/ 3 | /out/ 4 | .idea_modules/ 5 | 6 | ### Node template 7 | # Logs 8 | logs 9 | *.log 10 | npm-debug.log* 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | 17 | # Dependency directories 18 | node_modules 19 | jspm_packages 20 | 21 | # Optional npm cache directory 22 | .npm 23 | 24 | # Optional REPL history 25 | .node_repl_history 26 | 27 | ### Gradle template 28 | .gradle 29 | build/ 30 | 31 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 32 | !gradle-wrapper.jar 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [Konf Website](http://advantageous.github.io/konf/) 2 | 3 | # Konf - Typed Java Config system 4 | Java configuration library similar in concept to TypeSafe config, 5 | but uses full 6 | 7 | * YAML 8 | * JSON 9 | * JSON Lax 10 | * JavaScript (useful to create Config DSLs, and basic config logic) 11 | * Java Pojos (Pojos, Lists, Maps, basic types) 12 | * TypeSafe Config 13 | * Java properties 14 | 15 | You can also mix and match TypeSafe Config. 16 | 17 | ***Konf*** allows you to easily create your own 18 | [config DSLs](https://github.com/advantageous/konf/wiki/Config-Logic---creating-your-own-config-DSL) 19 | something that is not possible with ***TypeSafe Config***. 20 | 21 | 22 | ## Using Konf on your project 23 | 24 | Konf is in the [public maven repo](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22io.advantageous.konf%22). 25 | 26 | ### Using konf from maven 27 | ```xml 28 | 29 | io.advantageous.konf 30 | konf 31 | 1.3.0.RELEASE 32 | 33 | ``` 34 | 35 | ### Using konf from gradle 36 | ```java 37 | compile 'io.advantageous.konf:konf:1.3.0.RELEASE' 38 | ``` 39 | 40 | ### Using konf from scala sbt 41 | ```java 42 | libraryDependencies += "io.advantageous.konf" % "konf" % "1.3.0.RELEASE" 43 | ``` 44 | 45 | ### Using konf from clojure leiningen 46 | ```lisp 47 | [io.advantageous.konf/konf "1.3.0.RELEASE"] 48 | ``` 49 | 50 | Here is an example config for JavaScript. 51 | 52 | Konf expects the `config` variable to be set to a JavaScript object with 53 | properties. 54 | 55 | #### JavaScript based configuration for Java 56 | ```javascript 57 | var config = { 58 | 59 | myUri: uri("http://host:9000/path?foo=bar"), 60 | 61 | someKey: { 62 | nestedKey: 234, 63 | other: "this text" 64 | } 65 | 66 | }; 67 | ``` 68 | 69 | You can use full JavaScript for configuration as long as you define a 70 | variable called `config` that results in a JavaScript object which 71 | equates to a Java map. 72 | 73 | 74 | ## Defining your own DSL 75 | 76 | You can define you own config DSL for your environment. 77 | We have a [full example that shows you how to create a custom config DSL](https://github.com/advantageous/konf/wiki/Config-Logic---creating-your-own-config-DSL) 78 | for your internal projects. The example uses Mesosphere and Docker PORT 79 | look ups and it is from a real project. 80 | 81 | #### Defining your own config DSL 82 | ```javascript 83 | var config = { 84 | 85 | platform: { 86 | 87 | statsd: "udp://" + getDockerHost() + ":8125", 88 | 89 | servicePort: mesosPortAt(0, 8080), 90 | adminPort: mesosPortAt(1, 9090), 91 | ... 92 | ``` 93 | 94 | See the real world for [example that uses Konf to find ports under 95 | Mesosphere](https://github.com/advantageous/konf/wiki/Config-Logic---creating-your-own-config-DSL) 96 | (running in stating or prod) or under Docker (running on 97 | a local developers box). 98 | 99 | ## Overview 100 | 101 | * implemented in plain Java SDK almost no dependencies (sl4j, and reflekt with no others) 102 | * supports files in : YAML, JSON, JSON LAX, JavaScript, Java properties or any tree of Map/List basic types and POJOs 103 | * allows you to easily create your own config DSL 104 | * merges multiple configs across all formats 105 | * can load from configs, from classpath, http, file or just an Java Object tree 106 | * great support for "nesting" (treat any subtree of the config the same as the whole config) 107 | * users can override the config with Java system properties, java -Dmyapp.foo.bar=10 and sysProp 108 | * users can override the config with OS environment variables 109 | * supports configuring an app, with its framework and libraries, all from a single file such as application.yaml 110 | * parses duration and size settings, "512k" or "10 seconds" 111 | * converts types, so if you ask for a boolean and the value is the string "yes", or you ask for a float and the value is an int, it will figure it out. 112 | * API based on immutable Config instances, for thread safety and easy reasoning about config transformations 113 | * extensive test coverage 114 | 115 | This library limits itself to config. 116 | If you want to load config from another source, e.g., database or Redis or MongoDB, 117 | then you would need to write some custom code. The library has nice support for merging 118 | configurations (Configs with fall-backs) so if you build a custom Config 119 | from a custom source it's easy to merge it in. Just implement Config and then use 120 | `config(config...)` to configure your config into a chain of other configs. 121 | This is described at length below see "Loading config files with fallbacks". 122 | 123 | ## License 124 | 125 | The license is Apache 2.0. 126 | 127 | ## Release Notes 128 | 129 | Please see [Release Notes](https://github.com/advantageous/konf/releases), and 130 | [Release Notes In Progress](https://github.com/advantageous/konf/wiki/Release-Notes-Draft) 131 | for the latest releases. 132 | 133 | ## Build 134 | 135 | The build uses gradle and the tests are written in Java; and, 136 | the library itself is plain Java. 137 | 138 | ## Using the Library 139 | 140 | ```java 141 | import io.advantageous.config.ConfigLoader; 142 | 143 | Config conf = ConfigLoader.load("myconfig.js", "reference.js"); 144 | int bar1 = conf.getInt("foo.bar"); 145 | Config foo = conf.getConfig("foo"); 146 | int bar2 = foo.getInt("bar"); 147 | ``` 148 | 149 | ## Longer Examples 150 | 151 | You can see longer examples in [tests](https://github.com/advantageous/konf/blob/master/src/test/java/io/advantageous/config/JsLoadTest.java) 152 | along with [sample config](https://github.com/advantageous/konf/blob/master/src/test/resources/test-config.js). 153 | You can run these examples by `git cloning` this project and `running gradle test`. 154 | 155 | In brief, as shown in the examples: 156 | 157 | You create a Config instance provided by your application. 158 | You use `ConfigLoader.load()` and you can define your own config system. 159 | You could setup default `reference.yaml` or `reference.json` but you don't have to. 160 | You could just load a single level of config. Config is as complex or as simple 161 | as you need. 162 | 163 | A `Config` can be created with the parser methods in `ConfigLoader.load` 164 | or built up from any POJO object tree or tree of Map/List/Pojos basic value. 165 | It is very flexible. Examples are shown below and linked to below that use 166 | JSON, YAML and allow you to define your own `DSL` like config. 167 | It is very simple and easy to use. 168 | 169 | ## Immutability 170 | 171 | Objects are immutable, so methods on Config which transform the 172 | configuration return a new `Config`. 173 | There is no complex tree of `Config` objects. Just `Config`. 174 | It is pretty simple to use and understand. 175 | 176 | 177 | ## Java interface for Konf is Config. 178 | 179 | The Java interface for Konf is Config. 180 | You can get a sub Config from Config (`getConfig(path)`). 181 | The `path` is always in dot notation (`this.that.foo.bar`). 182 | You can also use: 183 | * `hasPath(path)` 184 | * `getInt(path)` 185 | * `getLong(path)` 186 | * `getDouble(path)` 187 | * `getBoolean(path)` can be true, false, "yes", "no", "on", "off", yes, no, off, on 188 | * `getString(path)` 189 | * `getStringList(path)` gets a list of strings 190 | * `getConfig(path)` gets a sub-config. 191 | * `getMap(path)` gets a map which is a sub-config. 192 | * `getConfigList(path)` gets a list of configs at the location specified. 193 | * `getIntList(path)` 194 | * `getLongList(path)` 195 | * `getDoubleList(path)` 196 | * `getBooleanList(path)` 197 | * `getDuration(path)` gets `java.time.Duration` useful for timeouts 198 | * `getDurationList(path)` gets duration list 199 | * `getUri(path)` gets `java.net.URI` useful for connecting to downstream services 200 | * `getUriList(path)` useful for connecting to downstream services 201 | 202 | 203 | The `getMap` works with JavaScript objects (or Java maps see below for loading config from Java objects, YAML or JSON). 204 | The `getStringList` and `getConfigList` works 205 | with JavaScript array of string and a JavaScript array of JavaScript objects. 206 | 207 | Note you get an exception if the `path` requested is not found. 208 | Use `hasPath(path)` if you think the config path might be missing. 209 | 210 | Here is partial glimpse at the `Config` interface. 211 | 212 | #### Config interface 213 | ```java 214 | public interface Config { 215 | 216 | /** Get string at location. */ 217 | String getString(String path); 218 | 219 | /** Checks to see if config has the path specified. */ 220 | boolean hasPath(String path); 221 | 222 | /** Get int at location. */ 223 | int getInt(String path); 224 | 225 | /** Get float at location. */ 226 | float getFloat(String path); 227 | 228 | /** Get double at location. */ 229 | double getDouble(String path); 230 | 231 | /** Get long at location. */ 232 | long getLong(String path); 233 | 234 | /** Get list of strings at location. */ 235 | List getStringList(String path); 236 | 237 | /** Get map at location. */ 238 | Map getMap(String path); 239 | 240 | /** Get a sub-config at location. */ 241 | Config getConfig(String path); 242 | 243 | /** Get list of sub-configs at location. */ 244 | List getConfigList(String path); 245 | 246 | /** Get a single POJO out of config at path. */ 247 | T get(String path, Class type); 248 | 249 | /** Get a list of POJOs. */ 250 | List getList(String path, Class componentType); 251 | 252 | /** Get duration. Good for timeouts */ 253 | Duration getDuration(String path); 254 | 255 | /** Get duration list. */ 256 | List getDurationList(String path); 257 | 258 | /** Get int list. */ 259 | List getIntegerList(String path); 260 | ... 261 | } 262 | 263 | ``` 264 | 265 | The `getX` methods work like you would expect. Given this config file. 266 | 267 | ## JavaScript functions for config 268 | 269 | #### JavaScript functions that we support 270 | * `sysProp(propName)` to read a sysProp as in `fooSize : sysProp("my.foo.size")` 271 | * `sysPropOrDefault(propName, defaultValue)` to read a sysProp or a default 272 | * `isWindowsOS()`, `isMacOS()`, `isUnix()`, `isLinux()`, `isSolaris()` 273 | * `env()` as in `fooSize : env('MY_FOO_SIZE')` or even `fooSize : sysPropOrDefault("my.foo.size", env('MY_FOO_SIZE'))` 274 | * `uri()` which creates a `java.net.URI` as in `fooURI : uri ("http://localhost:8080/")` 275 | * `java.time.Duration` is imported as `duration` 276 | * `java.lang.System` is imported as `system` 277 | * `seconds(units)`, `minutes(units)`, `hours(units)`, `days(units)`, `millis(units)` and `milliseconds(units`) define a `Duration` which is useful for configuring timeouts and interval jobs 278 | * constants `yes`, `no`, `on`, `off` for boolean config 279 | * `load(resources...)` load a config 280 | * `configs(config...)` chain a group of configs 281 | * `bytes(units)`, `kilobytes(units)`, `megabytes(units)`, `gigabytes(units)` to read sizes 282 | 283 | #### Sample config for testing and showing how config works 284 | 285 | ```javascript 286 | var config = { 287 | 288 | myUri: uri("http://host:9000/path?foo=bar"), 289 | 290 | someKey: { 291 | nestedKey: 234, 292 | other: "this text" 293 | }, 294 | 295 | int1: 1, 296 | float1: 1.0, 297 | double1: 1.0, 298 | long1: 1, 299 | string1: "rick", 300 | stringList: ['Foo', 'Bar'], 301 | configInner: { 302 | int2: 2, 303 | float2: 2.0 304 | }, 305 | uri: uri("http://localhost:8080/foo"), 306 | myClass: "java.lang.Object", 307 | myURI: "http://localhost:8080/foo", 308 | employee: {"id": 123, "name": "Geoff"}, 309 | employees: [ 310 | {id: 123, "name": "Geoff"}, 311 | {id: 456, "name": "Rick"}, 312 | {id: 789, 'name': "Paul"} 313 | ] 314 | }; 315 | 316 | ``` 317 | 318 | 319 | We can do the following operations. 320 | 321 | First we load the config. 322 | 323 | #### Loading the config. 324 | 325 | ```java 326 | 327 | private Config config; 328 | 329 | @Before 330 | public void setUp() throws Exception { 331 | config = ConfigLoader.load("test-config.js"); 332 | } 333 | ``` 334 | 335 | Note that `ConfigLoader.load(resources...)` takes a variable length string array. 336 | By default a resource String can contain a valid URI, which 337 | can have the scheme `classpath`, `file`, or `http`. If you do not specify 338 | a scheme than the path is assumed to be a classpath resource. 339 | 340 | #### Using different resources 341 | 342 | ```java 343 | config = ConfigLoader.load( 344 | "/io/mycompany/foo-classpath.js", 345 | "classpath:test-config.js", 346 | "classpath://foo.js", 347 | "classpath:/bar.js", 348 | "file://opt/app/config.js", 349 | "file:///opt/app/config2.js", 350 | "file:/opt/app/config.js", 351 | "http://my.internal.server:9090/foo.js" 352 | ); 353 | 354 | ``` 355 | 356 | Then we show reading basic types with the `config` object using `getX`. 357 | 358 | #### Reading basic types from config 359 | 360 | ```java 361 | @Test 362 | public void testSimple() throws Exception { 363 | 364 | //getInt 365 | assertEquals(1, config.getInt("int1")); 366 | 367 | //getStringList 368 | assertEquals(asList("Foo", "Bar"), 369 | config.getStringList("stringList")); 370 | 371 | //getString 372 | assertEquals("rick", config.getString("string1")); 373 | 374 | //getDouble 375 | assertEquals(1.0, config.getDouble("double1"), 0.001); 376 | 377 | //getLong 378 | assertEquals(1L, config.getLong("long1")); 379 | 380 | //getFloat 381 | assertEquals(1.0f, config.getFloat("float1"), 0.001); 382 | 383 | //Basic JDK value types are supported like class. 384 | assertEquals(Object.class, config.get("myClass", Class.class)); 385 | 386 | //Basic JDK value types are supported like URI. 387 | assertEquals(URI.create("http://localhost:8080/foo"), 388 | config.get("myURI", URI.class)); 389 | 390 | assertEquals(URI.create("http://localhost:8080/foo"), 391 | config.get("uri", URI.class)); 392 | 393 | } 394 | 395 | ``` 396 | 397 | You can work with nested properties as well. 398 | 399 | #### Reading a nested config from the config 400 | 401 | ```java 402 | @Test 403 | public void testGetConfig() throws Exception { 404 | //Read nested config. 405 | final Config configInner = config.getConfig("configInner"); 406 | assertEquals(2, configInner.getInt("int2")); 407 | assertEquals(2.0f, configInner.getFloat("float2"), 0.001); 408 | } 409 | 410 | @Test 411 | public void testGetMap() throws Exception { 412 | //Read nested config as a Java map. 413 | final Map map = config.getMap("configInner"); 414 | assertEquals(2, (int) map.get("int2")); 415 | assertEquals(2.0f, (double) map.get("float2"), 0.001); 416 | } 417 | ``` 418 | 419 | You can read deeply nested config items as well by specifying the 420 | property path using dot notation. 421 | 422 | #### Reading nested properties with dot notation from config 423 | 424 | 425 | ```java 426 | @Test 427 | public void testSimplePath() throws Exception { 428 | 429 | assertTrue(config.hasPath("configInner.int2")); 430 | assertFalse(config.hasPath("configInner.foo.bar")); 431 | assertEquals(2, config.getInt("configInner.int2")); 432 | assertEquals(2.0f, config.getFloat("configInner.float2"), 0.001); 433 | } 434 | ``` 435 | 436 | You can also read POJOs directly out of the config file. 437 | 438 | #### Reading a pojo directly out of the config file 439 | ```java 440 | 441 | @Test 442 | public void testReadClass() throws Exception { 443 | final Employee employee = config.get("employee", Employee.class); 444 | assertEquals("Geoff", employee.name); 445 | assertEquals("123", employee.id); 446 | } 447 | 448 | ``` 449 | 450 | You can read a list of POJOs at once. 451 | 452 | #### Reading a pojo list directly out of the config file 453 | ```java 454 | 455 | @Test 456 | public void testReadListOfClass() throws Exception { 457 | final List employees = config.getList("employees", Employee.class); 458 | assertEquals("Geoff", employees.get(0).name); 459 | assertEquals("123", employees.get(0).id); 460 | } 461 | ``` 462 | 463 | You can also read a list of config objects out of the config as well. 464 | 465 | #### Reading a config list directly out of the config file 466 | ```java 467 | 468 | final List employees = config.getConfigList("employees"); 469 | assertEquals("Geoff", employees.get(0).getString("name")); 470 | assertEquals("123", employees.get(0).getString("id")); 471 | ``` 472 | 473 | ## Using Config with YAML 474 | 475 | First include a YAML to object parser like [YAML Beans](https://github.com/EsotericSoftware/yamlbeans) 476 | or a library like this. 477 | 478 | #### Example YAML 479 | 480 | ```yaml 481 | name: Nathan Sweet 482 | age: 28 483 | address: 4011 16th Ave S 484 | phone numbers: 485 | - name: Home 486 | number: 206-555-5138 487 | - name: Work 488 | number: 425-555-2306 489 | ``` 490 | 491 | #### Using Konf with YAML 492 | 493 | ```java 494 | //Use YamlReader to load YAML file. 495 | YamlReader reader = new YamlReader(new FileReader("contact.yml")); 496 | 497 | //Convert object read from YAML into Konf config 498 | Config config = ConfigLoader.loadFromObject(reader.read()); 499 | 500 | //Now you have strongly typed access to fields 501 | String address = config.getString("address"); 502 | ``` 503 | 504 | You can also read Pojos from anywhere in the YAML file as well as 505 | sub configs. 506 | 507 | 508 | ## You can also use Konf with JSON using Boon 509 | 510 | See [Boon](https://github.com/advantageous/boon) JSON parser project, 511 | and [Boon in five minutes](https://github.com/boonproject/boon/wiki/Boon-JSON-in-five-minutes) 512 | 513 | #### Using Konf with JSON 514 | 515 | ```java 516 | ObjectMapper mapper = JsonFactory.create(); 517 | 518 | 519 | /* Convert object read from YAML into Konf config. 520 | 'src' can be File, InputStream, Reader, String. */ 521 | Config config = ConfigLoader.loadFromObject(mapper.fromJson(src)); 522 | 523 | 524 | //Now you have strongly typed access to fields 525 | String address = config.getString("address"); 526 | 527 | ``` 528 | 529 | Boon supports LAX JSON (Json with comments, and you do not need to quote 530 | the field). 531 | 532 | 533 | #### Working with java.time.Duration 534 | 535 | * `getDuration(path)` get a duration 536 | * `getDurationList(path)` get a duration list 537 | 538 | Konf supports "10 seconds" style config for duration as well as 539 | having built-in functions and support for ISO-8601. See documentation 540 | for [duration config](https://github.com/advantageous/konf/wiki/Working-with-Durations) 541 | for more details. 542 | 543 | ##### Konf can reads list of numbers. 544 | 545 | * `getIntList` reads list of ints 546 | * `getLongList` reads list of longs 547 | * `getDoubleList` reads list of doubles 548 | * `getFloatList` reads list of floats 549 | 550 | See documentation [list of number configuration](https://github.com/advantageous/konf/wiki/Working-with-lists-of-ints,-longs,-doubles,) 551 | for more details. 552 | 553 | #### Konf can read memory sizes 554 | 555 | * `getMemorySize(path)` 556 | * `getMemorySizeList(path)` 557 | 558 | 559 | 560 | This means we support config like: 561 | 562 | #### Sizes supported. 563 | ```javascript 564 | 565 | diskSpace : " 10 gigabytes", 566 | diskVolumes : [" 10 gigabytes", "10GB", "10 gigabytes", 10] 567 | ``` 568 | 569 | We support the following size Strings. 570 | 571 | #### Supported size strings 572 | ```java 573 | 574 | public enum MemorySizeUnit { 575 | 576 | BYTES(1, "B", "b", "byte", "bytes"), 577 | KILO_BYTES(1_000, "kB", "kilobyte", "kilobytes"), 578 | MEGA_BYTES(1_000_000, "MB", "megabyte", "megabytes"), 579 | GIGA_BYTES(1_000_000_000, "GB", "gigabyte", "gigabytes"), 580 | TERA_BYTES(1_000_000_000, "TB", "terabyte", "terabytes"), 581 | PETA_BYTES(1_000_000_000_000L, "PB", "petabyte", "petabytes"), 582 | EXA_BYTES(1_000_000_000_000_000L, "EB", "exabyte", "exabytes"), 583 | ZETTA_BYTES(1_000_000_000_000_000_000L, "ZB", "zettabyte", "zettabytes"); 584 | 585 | ``` 586 | 587 | You can also specify the sizes with built-in functions if you don't 588 | want to use strings. 589 | 590 | #### Using built-in functions to create sizes. 591 | ```javascript 592 | diskVolumes: [kilobytes(10), megabytes(10), bytes(10), gigabytes(10)] 593 | ``` 594 | 595 | ## Loading config files with fallbacks 596 | 597 | #### 598 | ```java 599 | 600 | import static io.advantageous.config.ConfigLoader.*; 601 | ... 602 | private Config config; 603 | ... 604 | config = configs(config("test-config.js"), config("reference.js")); 605 | 606 | ``` 607 | 608 | You can load config. The `config` method is an alias for `load(resources...)`. 609 | The `configs(config...)` creates a series of configs where the configs 610 | are search from left to right. The first config that has the object (starting 611 | from the left or 0 index) will return the object. 612 | 613 | Give the following two configs (from the above example). 614 | 615 | #### test-config.js 616 | ```javascript 617 | var config = { 618 | abc : "abc", 619 | ``` 620 | 621 | 622 | #### reference.js 623 | ```javascript 624 | var config = { 625 | abc : "abcFallback", 626 | def : "def" 627 | } 628 | ``` 629 | 630 | You could run this test. 631 | 632 | #### Testing the reference.js is a fallback for test-config.js. 633 | 634 | ```java 635 | 636 | import static io.advantageous.config.ConfigLoader.*; 637 | ... 638 | 639 | config = configs(config("test-config.js"), config("reference.js")); 640 | 641 | final String value = config.getString("abc"); 642 | assertEquals("abc", value); 643 | 644 | final String value1 = config.getString("def"); 645 | assertEquals("def", value1); 646 | ``` 647 | 648 | You can load your config anyway you like. The String `abc` is found 649 | when looking up the key `abc` because it is in the `test-config.js` which 650 | gets read before the value `abcFallback` which is in `reference.js`. 651 | Yet the `def` key yields the `"def"` because it is defined in `reference.js` 652 | but not `test-config.js`. You can implement the same style config reading and 653 | fallback as is in Type Safe Config but with your DSL. 654 | 655 | 656 | #### Using Konf with Typesafe Config 657 | 658 | This allows you to combine TypeSafe `Config` and Konf `Config`. 659 | You can have TypeSafe config be a fallback for Konf or the other way around. 660 | 661 | 662 | You can load TypeSafe `Config` as a Konf `Config` instance as follows: 663 | 664 | #### Loading Typesafe config as a Konf Config object 665 | ```java 666 | 667 | Config config = TypeSafeConfig.typeSafeConfig(); 668 | final String abc = config.getString("abc"); 669 | assertEquals("abc", abc); 670 | ``` 671 | 672 | You can also chain TypeSafe config as fallback or Konf `Config` as a fallback 673 | for TypeSafe `Config` as follows: 674 | 675 | 676 | #### Konf as a fallback for TypeSafe config. 677 | 678 | ```java 679 | 680 | 681 | import static io.advantageous.config.ConfigLoader.config; 682 | import static io.advantageous.config.ConfigLoader.configs; 683 | import static io.advantageous.config.ConfigLoader.load; 684 | 685 | ... 686 | 687 | Config config; 688 | ... 689 | config = configs(TypeSafeConfig.typeSafeConfig(), config("test-config.js")); 690 | ``` 691 | 692 | #### TypeSafe config as a fallback for Konf. 693 | 694 | ```java 695 | 696 | 697 | import static io.advantageous.config.ConfigLoader.config; 698 | import static io.advantageous.config.ConfigLoader.configs; 699 | import static io.advantageous.config.ConfigLoader.load; 700 | 701 | ... 702 | 703 | Config config; 704 | ... 705 | config = configs(config("test-config.js"), TypeSafeConfig.typeSafeConfig()); 706 | ``` 707 | 708 | You can convert any TypeSafe `Config` into a Konf `Config` by using 709 | `TypeSafeConfig.fromTypeSafeConfig(typeSafeConfig)`. 710 | 711 | Find out more about TypeSafe config support at [Konf TypeSafe config](http://advantageous.github.io/konf-typesafe-config/). 712 | 713 | #### Thanks 714 | 715 | If you like our configuration project, please try our 716 | [Reactive Java project](https://github.com/advantageous/reakt) 717 | or our [Actor based microservices lib](https://github.com/advantageous/qbit). 718 | 719 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | group 'io.advantageous.konf' 20 | version '1.3.8' 21 | 22 | apply plugin: 'java' 23 | apply plugin: 'maven' 24 | apply plugin: 'signing' 25 | apply plugin: 'idea' 26 | 27 | task wrapper(type: Wrapper) { 28 | gradleVersion = '2.11' 29 | } 30 | 31 | test.onlyIf { !Boolean.getBoolean('skip.tests') } 32 | 33 | repositories { 34 | mavenCentral() 35 | } 36 | 37 | sourceCompatibility = JavaVersion.VERSION_1_8 38 | targetCompatibility = JavaVersion.VERSION_1_8 39 | 40 | dependencies { 41 | compile 'org.slf4j:slf4j-api:1.7.14' 42 | compile 'io.advantageous.boon:boon-util:0.5.7' 43 | testCompile group: 'junit', name: 'junit', version: '4.11' 44 | } 45 | 46 | task javadocJar(type: Jar, dependsOn: javadoc) { 47 | classifier = 'javadoc' 48 | from 'build/docs/javadoc' 49 | } 50 | 51 | task sourcesJar(type: Jar) { 52 | from sourceSets.main.allSource 53 | classifier = 'sources' 54 | } 55 | 56 | artifacts { 57 | archives jar 58 | archives javadocJar 59 | archives sourcesJar 60 | } 61 | 62 | signing { 63 | required false 64 | sign configurations.archives 65 | } 66 | 67 | uploadArchives { 68 | repositories { 69 | mavenDeployer { 70 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 71 | repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { 72 | try { 73 | authentication(userName: sonatypeUsername, password: sonatypePassword) 74 | } catch (MissingPropertyException ignore) { 75 | } 76 | } 77 | 78 | pom.project { 79 | packaging 'jar' 80 | name project.name 81 | description "Java configuration using JavaScript." 82 | 83 | url 'https://github.com/advantageous/konf' 84 | 85 | scm { 86 | url 'scm:git@github.com:advantageous/konf.git' 87 | connection 'scm:git@github.com:advantageous/konf.git' 88 | developerConnection 'scm:git@github.com:advantageous/konf.git' 89 | } 90 | 91 | licenses { 92 | license { 93 | name 'The Apache Software License, Version 2.0' 94 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 95 | distribution 'repo' 96 | } 97 | } 98 | 99 | developers { 100 | developer { 101 | id 'richardHightower' 102 | name 'Richard Hightower' 103 | } 104 | developer { 105 | id 'sailorgeoffrey' 106 | name 'Geoffrey Chandler' 107 | } 108 | } 109 | } 110 | } 111 | } 112 | } 113 | 114 | 115 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/advantageous/konf/51f773fdee0467d8e4b3aaed3567b8b40b5ca9a7/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Apr 26 17:51:47 PDT 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.11-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This settings file was auto generated by the Gradle buildInit task 3 | * by 'gcc' at '4/26/16 5:49 PM' with Gradle 2.11 4 | * 5 | * The settings file is used to specify which projects to include in your build. 6 | * In a single project build this file can be empty or even removed. 7 | * 8 | * Detailed information about configuring a multi-project build in Gradle can be found 9 | * in the user guide at https://docs.gradle.org/2.11/userguide/multi_project_builds.html 10 | */ 11 | 12 | /* 13 | // To declare projects as part of a multi-project build use the 'include' method 14 | include 'shared' 15 | include 'api' 16 | include 'services:webservice' 17 | */ 18 | 19 | rootProject.name = 'konf' 20 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/config/Config.java: -------------------------------------------------------------------------------- 1 | package io.advantageous.config; 2 | 3 | import java.net.URI; 4 | import java.time.Duration; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | /** 9 | * Configuration Interface 10 | * 11 | * @author Rick Hightower 12 | * @author Geoff Chandler 13 | */ 14 | @SuppressWarnings("WeakerAccess") 15 | public interface Config { 16 | 17 | 18 | /** 19 | * Get a ConfigMemorySize at location 20 | * 21 | * @param path path 22 | * @return string at location. 23 | */ 24 | ConfigMemorySize getMemorySize(String path); 25 | 26 | /** 27 | * Get a ConfigMemorySize list at location 28 | * 29 | * @param path path 30 | * @return string at location. 31 | */ 32 | List getMemorySizeList(String path); 33 | 34 | /** 35 | * Get a URI at location 36 | * 37 | * @param path path 38 | * @return string at location./ 39 | */ 40 | URI getUri(String path); 41 | 42 | /** 43 | * Get a list of URI at location 44 | * 45 | * @param path path 46 | * @return string at location. 47 | */ 48 | List getUriList(String path); 49 | 50 | 51 | /** 52 | * Get a string at location 53 | * 54 | * @param path path 55 | * @return string at location./ 56 | */ 57 | String getString(String path); 58 | 59 | /** 60 | * Checks to see if config has the path specified. 61 | * 62 | * @param path path to property. 63 | * @return true if the path exists. 64 | */ 65 | boolean hasPath(String path); 66 | 67 | /** 68 | * Get int at location 69 | * 70 | * @param path path to property. 71 | * @return value 72 | */ 73 | int getInt(String path); 74 | 75 | /** 76 | * Get boolean at location 77 | * 78 | * @param path path to property. 79 | * @return value 80 | */ 81 | boolean getBoolean(String path); 82 | 83 | 84 | /** 85 | * get a boolean list 86 | * 87 | * @param path path to boolean list 88 | * @return boolean list 89 | */ 90 | List getBooleanList(String path); 91 | 92 | /** 93 | * Get float at location 94 | * 95 | * @param path path to property. 96 | * @return value 97 | */ 98 | float getFloat(String path); 99 | 100 | /** 101 | * Get double at location 102 | * 103 | * @param path path to property. 104 | * @return value 105 | */ 106 | double getDouble(String path); 107 | 108 | /** 109 | * Get long at location 110 | * 111 | * @param path path to property. 112 | * @return value 113 | */ 114 | long getLong(String path); 115 | 116 | 117 | /** 118 | * Get Duration at location 119 | * 120 | * @param path path to property. 121 | * @return value 122 | */ 123 | Duration getDuration(String path); 124 | 125 | /** 126 | * Get Duration at location 127 | * 128 | * @param path path to property. 129 | * @return value 130 | */ 131 | List getDurationList(String path); 132 | 133 | /** 134 | * Get list of strings at location 135 | * 136 | * @param path path to list of strings. 137 | * @return value 138 | */ 139 | List getStringList(String path); 140 | 141 | /** 142 | * Get list of ints at location 143 | * 144 | * @param path path to list of strings. 145 | * @return value 146 | */ 147 | List getIntList(String path); 148 | 149 | /** 150 | * Get list of doubles at location 151 | * 152 | * @param path path to list of strings. 153 | * @return value 154 | */ 155 | List getDoubleList(String path); 156 | 157 | /** 158 | * Get list of floats at location 159 | * 160 | * @param path path to list of strings. 161 | * @return value 162 | */ 163 | List getFloatList(String path); 164 | 165 | /** 166 | * Get list of doubles at location 167 | * 168 | * @param path path to list of strings. 169 | * @return value 170 | */ 171 | List getLongList(String path); 172 | 173 | /** 174 | * Get map at location 175 | * 176 | * @param path path to list of strings. 177 | * @return value 178 | */ 179 | Map getMap(String path); 180 | 181 | /** 182 | * Get a single config at location. 183 | * 184 | * @param path path to config 185 | * @return Config at location 186 | */ 187 | Config getConfig(String path); 188 | 189 | 190 | /** 191 | * Get list of configs at location. 192 | * 193 | * @param path path to config 194 | * @return Config at location 195 | */ 196 | List getConfigList(String path); 197 | 198 | /** 199 | * Get a single POJO. 200 | * 201 | * @param path path 202 | * @param type type 203 | * @param T generic type 204 | * @return a single POJO. 205 | */ 206 | T get(String path, Class type); 207 | 208 | /** 209 | * Get a list of POJOs. 210 | * 211 | * @param path path 212 | * @param componentType component type of the pojo 213 | * @param generic type of the POJO. 214 | * @return list of POJOs of type T (type class). 215 | */ 216 | List getList(String path, Class componentType); 217 | 218 | } 219 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/config/ConfigFromObject.java: -------------------------------------------------------------------------------- 1 | package io.advantageous.config; 2 | 3 | import io.advantageous.boon.core.Conversions; 4 | import io.advantageous.boon.core.Sets; 5 | import io.advantageous.boon.core.Value; 6 | import io.advantageous.boon.core.reflection.Mapper; 7 | import io.advantageous.boon.core.reflection.MapperSimple; 8 | import jdk.nashorn.api.scripting.ScriptObjectMirror; 9 | 10 | import java.math.BigDecimal; 11 | import java.net.URI; 12 | import java.time.Duration; 13 | import java.time.format.DateTimeParseException; 14 | import java.util.*; 15 | import java.util.concurrent.TimeUnit; 16 | import java.util.stream.Collectors; 17 | 18 | import static io.advantageous.boon.core.Maps.map; 19 | import static io.advantageous.boon.core.reflection.BeanUtils.findProperty; 20 | import static java.util.Arrays.asList; 21 | import static java.util.concurrent.TimeUnit.*; 22 | 23 | /** 24 | * Turns any Map or Java Object into config. 25 | * Works with any Java Object tree and or any Nashorn ScriptObjectMirror tree. 26 | * 27 | * @author Rick Hightower 28 | * @author Geoff Chandler 29 | */ 30 | class ConfigFromObject implements Config { 31 | 32 | private final Object root; 33 | private final Mapper mapper = new MapperSimple(); 34 | 35 | private final Map> timeUnitMap = map( 36 | MICROSECONDS, asList("microseconds", "microsecond", "micros", "micro", "us"), 37 | MILLISECONDS, asList("milliseconds", "millisecond", "millis", "milli", "ms"), 38 | SECONDS, asList("seconds", "second", "s"), 39 | MINUTES, asList("minutes", "minute", "m"), 40 | HOURS, asList("hours", "hour", "h"), 41 | DAYS, asList("days", "day", "d") 42 | ); 43 | 44 | 45 | private final Set TRUE = Sets.set("yes", "true", "on"); 46 | private final Set FALSE = Sets.set("no", "false", "off"); 47 | 48 | ConfigFromObject(T object) { 49 | this.root = object; 50 | } 51 | 52 | 53 | @Override 54 | @SuppressWarnings("unchecked") 55 | public T get(String path, Class type) { 56 | final Object value = validatePath(path); 57 | if (type.isAssignableFrom(value.getClass())) { 58 | return (T) value; 59 | } else if (value instanceof Map) { 60 | final Map map = getMap(path); 61 | return mapper.fromMap(map, type); 62 | } else { 63 | return Conversions.coerce(type, value); 64 | } 65 | } 66 | 67 | @Override 68 | @SuppressWarnings("unchecked") 69 | public List getList(String path, Class componentType) { 70 | 71 | Object value = validatePath(path); 72 | if (value instanceof ScriptObjectMirror) { 73 | value = extractListFromScriptObjectMirror(path, value, Map.class); 74 | } 75 | 76 | if (value instanceof List) { 77 | List list = (List) value; 78 | return mapper.convertListOfMapsToObjects(list, componentType); 79 | } else { 80 | throw new IllegalArgumentException("Path must resolve to a java.util.List path = " + path); 81 | } 82 | } 83 | 84 | @Override 85 | public ConfigMemorySize getMemorySize(String path) { 86 | final Object value = validatePath(path); 87 | return convertObjectToMemorySize(path, value); 88 | } 89 | 90 | 91 | @Override 92 | public List getMemorySizeList(String path) { 93 | final Object value = validatePath(path); 94 | final Object object = extractListFromScriptObjectMirror(path, value, Object.class); 95 | @SuppressWarnings("unchecked") 96 | final List list = (List) object; 97 | return list.stream().map(o -> convertObjectToMemorySize(path, o)).collect(Collectors.toList()); 98 | } 99 | 100 | @Override 101 | public URI getUri(String path) { 102 | Object value = validatePath(path); 103 | return convertToUri(path, value); 104 | } 105 | 106 | @Override 107 | public String getString(String path) { 108 | Object value = validatePath(path); 109 | if (!(value instanceof CharSequence)) { 110 | throw new IllegalArgumentException("The path " + path + " does not equate to a string " + value); 111 | } 112 | return value.toString(); 113 | } 114 | 115 | @Override 116 | public boolean hasPath(String path) { 117 | return findProperty(root, path) != null; 118 | } 119 | 120 | @Override 121 | public int getInt(String path) { 122 | return validateNumberInPath(path).intValue(); 123 | } 124 | 125 | @Override 126 | public float getFloat(String path) { 127 | return validateNumberInPath(path).floatValue(); 128 | } 129 | 130 | @Override 131 | public double getDouble(String path) { 132 | return validateNumberInPath(path).doubleValue(); 133 | } 134 | 135 | @Override 136 | public long getLong(String path) { 137 | return validateNumberInPath(path).longValue(); 138 | } 139 | 140 | 141 | @Override 142 | public List getBooleanList(String path) { 143 | final Object value = validatePath(path); 144 | final Object object = extractListFromScriptObjectMirror(path, value, Object.class); 145 | 146 | @SuppressWarnings("unchecked") 147 | final List list = (List) object; 148 | return list.stream().map(o -> convertObjectToBoolean(path, o)).collect(Collectors.toList()); 149 | } 150 | 151 | 152 | @Override 153 | public List getUriList(String path) { 154 | final Object value = validatePath(path); 155 | final Object object = extractListFromScriptObjectMirror(path, value, Object.class); 156 | 157 | @SuppressWarnings("unchecked") 158 | final List list = (List) object; 159 | return list.stream().map(o -> convertToUri(path, o)).collect(Collectors.toList()); 160 | } 161 | 162 | @Override 163 | public boolean getBoolean(String path) { 164 | return convertObjectToBoolean(path, validatePath(path)); 165 | } 166 | 167 | @Override 168 | public Duration getDuration(final String path) { 169 | return convertObjectToDuration(path, validatePath(path)); 170 | } 171 | 172 | @Override 173 | public List getDurationList(String path) { 174 | validatePath(path); 175 | final Object value = findProperty(root, path); 176 | final Object object = extractListFromScriptObjectMirror(path, value, Object.class); 177 | @SuppressWarnings("unchecked") 178 | final List list = (List) object; 179 | return list.stream().map(o -> convertObjectToDuration(path, o)).collect(Collectors.toList()); 180 | } 181 | 182 | 183 | @Override 184 | @SuppressWarnings("unchecked") 185 | public Map getMap(final String path) { 186 | return (Map) validatePath(path); 187 | } 188 | 189 | @Override 190 | @SuppressWarnings("unchecked") 191 | public List getStringList(final String path) { 192 | Object value = validatePath(path); 193 | if (value instanceof ScriptObjectMirror) { 194 | value = extractListFromScriptObjectMirror(path, value, CharSequence.class); 195 | } 196 | return ((List) value).stream().map((CharSequence::toString)).collect(Collectors.toList()); 197 | } 198 | 199 | @Override 200 | public List getIntList(final String path) { 201 | return getNumberList(path).stream().map(Number::intValue).collect(Collectors.toList()); 202 | } 203 | 204 | @Override 205 | public List getDoubleList(final String path) { 206 | return getNumberList(path).stream().map(Number::doubleValue).collect(Collectors.toList()); 207 | } 208 | 209 | @Override 210 | public List getFloatList(final String path) { 211 | return getNumberList(path).stream().map(Number::floatValue).collect(Collectors.toList()); 212 | } 213 | 214 | @Override 215 | public List getLongList(final String path) { 216 | return getNumberList(path).stream().map(Number::longValue).collect(Collectors.toList()); 217 | } 218 | 219 | 220 | @SuppressWarnings("unchecked") 221 | private List getNumberList(final String path) { 222 | Object value = validatePath(path); 223 | if (value instanceof ScriptObjectMirror) { 224 | value = extractListFromScriptObjectMirror(path, value, Number.class); 225 | } 226 | 227 | if (value instanceof List) { 228 | 229 | List list = (List) value; 230 | return list.stream().filter(o -> { 231 | if (!(o instanceof Number || o instanceof CharSequence)) { 232 | throw new IllegalArgumentException("Path must equate to list with Numbers or Strings" + 233 | " that can be parsed to numbers," + 234 | " but found type " + (o == null ? null : o.getClass().getName())); 235 | } 236 | return true; 237 | }).map(o -> convertObjectToNumber(path, o)).collect(Collectors.toList()); 238 | } else { 239 | throw new IllegalArgumentException("Path must equate to list with Numbers or Strings" + 240 | " that can be parsed to numbers"); 241 | } 242 | } 243 | 244 | 245 | @Override 246 | public Config getConfig(final String path) { 247 | return new ConfigFromObject(getMap(path)); 248 | } 249 | 250 | @Override 251 | @SuppressWarnings("unchecked") 252 | public List getConfigList(final String path) { 253 | Object value = validatePath(path); 254 | if (value instanceof ScriptObjectMirror) { 255 | value = extractListFromScriptObjectMirror(path, value, Map.class); 256 | } 257 | if (!(value instanceof List)) { 258 | throw new IllegalArgumentException("Expecting list at location " + path + "but found " + value.getClass()); 259 | } 260 | final List list = (List) value; 261 | if (list.stream().anyMatch(o -> !(o instanceof Map))) { 262 | throw new IllegalArgumentException("List must contain config maps only for path " + path); 263 | } 264 | return list.stream().map(o -> (Map) o) 265 | .map(ConfigFromObject::new) 266 | .collect(Collectors.toList()); 267 | 268 | } 269 | 270 | 271 | private Duration convertObjectToDuration(String path, Object value) { 272 | /* It is already a duration so just return it. */ 273 | if (value instanceof Duration) { 274 | return (Duration) value; 275 | } 276 | 277 | /* It is a number with no postfix so assume milliseconds. */ 278 | if (value instanceof Number) { 279 | return Duration.ofMillis(((Number) value).longValue()); 280 | } 281 | return convertStringToDuration(path, value); 282 | } 283 | 284 | 285 | private URI convertToUri(String path, Object value) { 286 | if (value instanceof URI) { 287 | return (URI) value; 288 | } else if (value instanceof CharSequence) { 289 | try { 290 | return URI.create(value.toString()); 291 | } catch (Exception ex) { 292 | throw new IllegalArgumentException("The path " + path + " could not be parse into a valid URI " + value); 293 | } 294 | } else { 295 | throw new IllegalArgumentException("The path " + path + " does not equate to a URI " + value); 296 | } 297 | } 298 | 299 | private boolean convertObjectToBoolean(String path, Object property) { 300 | if (!(property instanceof Boolean) && !(property instanceof CharSequence) && !(property instanceof Value)) { 301 | throw new IllegalArgumentException("Path " + path + " must resolve to a boolean like type value = \"" 302 | + property + "/"); 303 | } 304 | if (property instanceof Boolean) { 305 | return (Boolean) property; 306 | } 307 | if (property instanceof Value) { 308 | return ((Value) property).booleanValue(); 309 | } 310 | 311 | final String propValue = property.toString(); 312 | if (TRUE.contains(propValue)) { 313 | return true; 314 | } else if (FALSE.contains(propValue)) { 315 | return false; 316 | } else { 317 | throw new IllegalArgumentException("Path " + path + " must resolve to a boolean like type value = \"" 318 | + propValue + "/"); 319 | } 320 | } 321 | 322 | 323 | private Object validatePath(String path) { 324 | Object value = findProperty(root, path); 325 | if (value == null) { 326 | throw new IllegalArgumentException("Path or property " + path + " does not exist"); 327 | } 328 | return value; 329 | } 330 | 331 | private Number validateNumberInPath(String path) { 332 | final Object object = findProperty(root, path); 333 | return convertObjectToNumber(path, object); 334 | } 335 | 336 | private Number convertObjectToNumber(String path, Object object) { 337 | if (object == null) { 338 | throw new IllegalArgumentException("Path or property " + path + " does not exist"); 339 | } 340 | 341 | if (object instanceof CharSequence) { 342 | try { 343 | return new BigDecimal(object.toString()); 344 | } catch (Exception ex) { 345 | throw new IllegalArgumentException("Path or property " + path + " exists but is not a number value =" 346 | + object); 347 | } 348 | } 349 | 350 | if (object instanceof Number) { 351 | return (Number) object; 352 | } 353 | 354 | throw new IllegalArgumentException("Path or property " + path + " exists but is not a number value =" 355 | + object); 356 | } 357 | 358 | private Duration convertStringToDuration(final String path, final Object value) { 359 | /* It is some sort of string like thing. */ 360 | if (value instanceof CharSequence) { 361 | final String durationString = value.toString(); //Make it an actual string. 362 | /* try to parse it as a ISO-8601 duration format. */ 363 | try { 364 | return Duration.parse(value.toString()); 365 | } catch (DateTimeParseException dateTimeParse) { 366 | /* If it is not ISO-8601 format assume it is typesafe config spec. format. */ 367 | return parseDurationUsingTypeSafeSpec(path, durationString); 368 | } 369 | } else { 370 | throw new IllegalArgumentException("Path " + path + " does not resolve to a duration for value " + value); 371 | } 372 | } 373 | 374 | /** 375 | * Parses a string into duration type safe spec format if possible. 376 | * 377 | * @param path property path 378 | * @param durationString duration string using "10 seconds", "10 days", etc. format from type safe. 379 | * @return Duration parsed from typesafe config format. 380 | */ 381 | private Duration parseDurationUsingTypeSafeSpec(final String path, final String durationString) { 382 | 383 | /* Check to see if any of the postfixes are at the end of the durationString. */ 384 | final Optional>> entry = timeUnitMap.entrySet().stream() 385 | .filter(timeUnitListEntry -> 386 | /* Go through values in map and see if there are any matches. */ 387 | timeUnitListEntry.getValue() 388 | .stream() 389 | .anyMatch(durationString::endsWith)) 390 | .findFirst(); 391 | 392 | /* if we did not match any postFixes then exit early with an exception. */ 393 | if (!entry.isPresent()) { 394 | throw new IllegalArgumentException("Path " + path + " does not resolve to a duration " + durationString); 395 | } 396 | 397 | /* Convert the value to a Duration. 398 | */ 399 | Optional optional = entry.map(timeUnitListEntry -> { 400 | 401 | /* Find the prefix that matches the best. Prefixes are ordered by length. 402 | * Biggest prefixes are matched first. 403 | */ 404 | final Optional postFix = timeUnitListEntry 405 | .getValue() 406 | .stream() 407 | .filter(durationString::endsWith) 408 | .findFirst(); 409 | 410 | if (postFix.isPresent()) { 411 | /* Remove the prefix from the string so only the unit remains. */ 412 | final String unitString = durationString.replace(postFix.get(), "").trim(); 413 | 414 | /* Try to parse the units, if the units do not parse than they gave us a bad prefix. */ 415 | try { 416 | long unit = Long.parseLong(unitString); 417 | return Duration.ofNanos(timeUnitListEntry.getKey().toNanos(unit)); 418 | } catch (NumberFormatException nfe) { 419 | throw new IllegalArgumentException("Path does not resolve to a duration " + durationString); 420 | } 421 | } else { 422 | throw new IllegalArgumentException("Path does not resolve to a duration " + durationString); 423 | } 424 | }); 425 | if (optional.isPresent()) { 426 | return optional.get(); 427 | } else { 428 | throw new IllegalArgumentException("Path does not resolve to a duration " + durationString); 429 | } 430 | } 431 | 432 | @SuppressWarnings("unchecked") 433 | private Object extractListFromScriptObjectMirror(final String path, final Object value, final Class typeCheck) { 434 | 435 | if (value instanceof ScriptObjectMirror) { 436 | final ScriptObjectMirror mirror = ((ScriptObjectMirror) value); 437 | if (!mirror.isArray()) { 438 | throw new IllegalArgumentException("Path must resolve to a JS array or java.util.List path = " + path); 439 | } 440 | List list = new ArrayList(mirror.size()); 441 | for (int index = 0; index < mirror.size(); index++) { 442 | final Object item = mirror.getSlot(index); 443 | 444 | if (item == null) { 445 | throw new IllegalArgumentException("Path must resolve to a list of " + typeCheck.getName() 446 | + " issue at index " + index + " but item is null path is " + path); 447 | } 448 | if (!typeCheck.isAssignableFrom(item.getClass())) { 449 | throw new IllegalArgumentException("Path must resolve to a list of " + typeCheck.getName() 450 | + " issue at index " + index + "but item is " + item.getClass().getName() + " path is " + path); 451 | } 452 | list.add(item); 453 | } 454 | return list; 455 | } else if (value instanceof List) { 456 | return value; 457 | } else { 458 | throw new IllegalArgumentException("Path must resolve to a JS array or java.util.List path = " + path); 459 | } 460 | } 461 | 462 | 463 | private ConfigMemorySize convertObjectToMemorySize(String path, Object value) { 464 | if (value instanceof CharSequence) { 465 | return ConfigMemorySize.valueOf(value.toString()); 466 | } else if (value instanceof Number) { 467 | return new ConfigMemorySize(MemorySizeUnit.BYTES, ((Number) value).longValue()); 468 | } else if (value instanceof ConfigMemorySize) { 469 | return ((ConfigMemorySize) value); 470 | } 471 | 472 | throw new IllegalArgumentException("Path must resolve to a MemorySize path = " + path + " value = " + value); 473 | } 474 | 475 | 476 | @Override 477 | public String toString() { 478 | return "ConfigFromObject{" + 479 | "root=" + root + 480 | '}'; 481 | } 482 | } 483 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/config/ConfigLoader.java: -------------------------------------------------------------------------------- 1 | package io.advantageous.config; 2 | 3 | import javax.script.ScriptEngine; 4 | import javax.script.ScriptEngineManager; 5 | import javax.script.ScriptException; 6 | import java.io.InputStream; 7 | import java.io.InputStreamReader; 8 | 9 | import static io.advantageous.config.ResourceUtils.findResource; 10 | 11 | /** 12 | * Javascript configuration loader. 13 | * 14 | * @author Geoff Chandler geoffc@gmail.com 15 | * @author Rick Hightower 16 | */ 17 | @SuppressWarnings("WeakerAccess") 18 | public class ConfigLoader { 19 | 20 | /** 21 | * Do not allow instantiation of this class. 22 | */ 23 | private ConfigLoader() { 24 | throw new IllegalStateException("this class is not to be instantiated."); 25 | } 26 | 27 | /** 28 | * Works with any nested structure of maps and lists and basic Java types. 29 | * Works with Pojos, Maps, Lists. 30 | * 31 | * @param rootOfConfig rootConfig object 32 | * @return Config version of object map. 33 | */ 34 | public static Config loadFromObject(final Object rootOfConfig) { 35 | return new ConfigFromObject(rootOfConfig); 36 | } 37 | 38 | /** 39 | * Creates a chain of configs. 40 | * 41 | * @param configs var args of configs. Searched from left to right so that the 42 | * left side overrides the right side. 43 | * @return chain of configs as a single config. Left side is most significant. 44 | */ 45 | public static Config configWithFallbacks(final Config[] configs) { 46 | return new Configs(configs); 47 | } 48 | 49 | /** 50 | * Alias for `configWithFallbacks`. 51 | * Creates a chain of configs. 52 | * 53 | * @param configs var args of configs. Searched from left to right so that the 54 | * left side overrides the right side. 55 | * @return chain of configs as a single config. Left side is most significant. 56 | */ 57 | public static Config configs(final Config... configs) { 58 | return configWithFallbacks(configs); 59 | } 60 | 61 | /** 62 | * Alias for `load`. Loads a config file. 63 | * 64 | * @param resources classpath resources to from which to load javascript 65 | * @return Config. 66 | */ 67 | public static Config config(final String... resources) { 68 | return load(resources); 69 | } 70 | 71 | /** 72 | * Loads a config file. 73 | * 74 | * @param resources classpath resources to from which to load javascript 75 | * @return Config. 76 | */ 77 | public static Config load(final String... resources) { 78 | final ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); 79 | try (final InputStream resourceAsStream = findResource("jjs-config-utils.js")) { 80 | engine.eval(new InputStreamReader(resourceAsStream)); 81 | loadResources(engine, resources); 82 | } catch (final ScriptException se) { 83 | throw new IllegalArgumentException("unable to execute main javascript.", se); 84 | } catch (final Exception ex) { 85 | if (ex instanceof IllegalArgumentException) { 86 | throw (IllegalArgumentException) ex; 87 | } 88 | throw new IllegalArgumentException("unable to load main resource ", ex); 89 | } 90 | return loadFromObject(engine.get("config")); 91 | } 92 | 93 | private static void loadResources(ScriptEngine engine, String[] resources) { 94 | for (final String resource : resources) { 95 | try (final InputStream resource1 = findResource(resource)) { 96 | engine.eval(new InputStreamReader(resource1)); 97 | } catch (final Exception e) { 98 | throw new IllegalArgumentException("unable to execute javascript. " + resource, e); 99 | } 100 | } 101 | } 102 | 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/config/ConfigMemorySize.java: -------------------------------------------------------------------------------- 1 | package io.advantageous.config; 2 | 3 | /** 4 | * An immutable class representing an amount of memory. 5 | */ 6 | @SuppressWarnings("WeakerAccess") 7 | public class ConfigMemorySize { 8 | 9 | private final MemorySizeUnit memorySizeUnit; 10 | private final long amount; 11 | 12 | /** 13 | * Used to create an instance of ConfigMemorySize. 14 | * Must be public used by TypeSafe config wrapper. 15 | * 16 | * @param memorySizeUnit memorySizeUnit 17 | * @param amount amount 18 | */ 19 | public ConfigMemorySize(MemorySizeUnit memorySizeUnit, long amount) { 20 | this.memorySizeUnit = memorySizeUnit; 21 | this.amount = amount; 22 | } 23 | 24 | /** 25 | * Parse the value and returns a ConfigMemorySize 26 | * 27 | * @param value value of string 28 | * @return value of ConfigMemorySize 29 | */ 30 | public static ConfigMemorySize valueOf(final String value) { 31 | return MemorySizeUnit.parseToConfigMemorySize(value); 32 | } 33 | 34 | public long toBytes() { 35 | return memorySizeUnit.toLong(amount); 36 | } 37 | 38 | @Override 39 | public boolean equals(Object o) { 40 | if (this == o) return true; 41 | if (o == null || getClass() != o.getClass()) return false; 42 | 43 | ConfigMemorySize that = (ConfigMemorySize) o; 44 | 45 | if (amount != that.amount) return false; 46 | return memorySizeUnit == that.memorySizeUnit; 47 | 48 | } 49 | 50 | @Override 51 | public int hashCode() { 52 | int result = memorySizeUnit != null ? memorySizeUnit.hashCode() : 0; 53 | result = 31 * result + (int) (amount ^ (amount >>> 32)); 54 | return result; 55 | } 56 | 57 | @Override 58 | public String toString() { 59 | return "ConfigMemorySize{" + 60 | "memorySizeUnit=" + memorySizeUnit + 61 | ", amount=" + amount + 62 | '}'; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/config/Configs.java: -------------------------------------------------------------------------------- 1 | package io.advantageous.config; 2 | 3 | import java.net.URI; 4 | import java.time.Duration; 5 | import java.util.*; 6 | 7 | class Configs implements Config { 8 | 9 | private final List configList; 10 | 11 | Configs(Config... configs) { 12 | this.configList = Collections.unmodifiableList(Arrays.asList(configs)); 13 | } 14 | 15 | 16 | @Override 17 | public ConfigMemorySize getMemorySize(final String path) { 18 | return findConfig(path).getMemorySize(path); 19 | } 20 | 21 | private Config findConfig(String path) { 22 | final Optional configOptional = 23 | configList.stream().filter(config -> config.hasPath(path)).findFirst(); 24 | if (!configOptional.isPresent()) { 25 | throw new IllegalArgumentException("Path path = " + path + " not found."); 26 | } 27 | return configOptional.get(); 28 | } 29 | 30 | @Override 31 | public List getMemorySizeList(String path) { 32 | return findConfig(path).getMemorySizeList(path); 33 | } 34 | 35 | @Override 36 | public URI getUri(String path) { 37 | return findConfig(path).getUri(path); 38 | } 39 | 40 | @Override 41 | public List getUriList(String path) { 42 | return findConfig(path).getUriList(path); 43 | } 44 | 45 | @Override 46 | public String getString(String path) { 47 | return findConfig(path).getString(path); 48 | } 49 | 50 | @Override 51 | public boolean hasPath(String path) { 52 | return configList.stream().filter(config -> config.hasPath(path)).findFirst().isPresent(); 53 | } 54 | 55 | @Override 56 | public int getInt(String path) { 57 | return findConfig(path).getInt(path); 58 | } 59 | 60 | @Override 61 | public boolean getBoolean(String path) { 62 | return findConfig(path).getBoolean(path); 63 | } 64 | 65 | @Override 66 | public List getBooleanList(String path) { 67 | return findConfig(path).getBooleanList(path); 68 | } 69 | 70 | @Override 71 | public float getFloat(String path) { 72 | return findConfig(path).getFloat(path); 73 | } 74 | 75 | @Override 76 | public double getDouble(String path) { 77 | return findConfig(path).getDouble(path); 78 | } 79 | 80 | @Override 81 | public long getLong(String path) { 82 | return findConfig(path).getLong(path); 83 | } 84 | 85 | @Override 86 | public Duration getDuration(String path) { 87 | return findConfig(path).getDuration(path); 88 | } 89 | 90 | @Override 91 | public List getDurationList(String path) { 92 | return findConfig(path).getDurationList(path); 93 | } 94 | 95 | @Override 96 | public List getStringList(String path) { 97 | return findConfig(path).getStringList(path); 98 | } 99 | 100 | @Override 101 | public List getIntList(String path) { 102 | return findConfig(path).getIntList(path); 103 | } 104 | 105 | @Override 106 | public List getDoubleList(String path) { 107 | return findConfig(path).getDoubleList(path); 108 | } 109 | 110 | @Override 111 | public List getFloatList(String path) { 112 | return findConfig(path).getFloatList(path); 113 | } 114 | 115 | @Override 116 | public List getLongList(String path) { 117 | return findConfig(path).getLongList(path); 118 | } 119 | 120 | @Override 121 | public Map getMap(String path) { 122 | return findConfig(path).getMap(path); 123 | } 124 | 125 | @Override 126 | public Config getConfig(String path) { 127 | return findConfig(path).getConfig(path); 128 | } 129 | 130 | @Override 131 | public List getConfigList(String path) { 132 | return findConfig(path).getConfigList(path); 133 | } 134 | 135 | @Override 136 | public T get(String path, Class type) { 137 | return findConfig(path).get(path, type); 138 | } 139 | 140 | @Override 141 | public List getList(String path, Class componentType) { 142 | return findConfig(path).getList(path, componentType); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/config/MemorySizeUnit.java: -------------------------------------------------------------------------------- 1 | package io.advantageous.config; 2 | 3 | import java.util.*; 4 | 5 | import static java.lang.Long.parseLong; 6 | 7 | public enum MemorySizeUnit { 8 | 9 | BYTES(1, "B", "b", "byte", "bytes"), 10 | KILO_BYTES(1_000, "kB", "kilobyte", "kilobytes"), 11 | MEGA_BYTES(1_000_000, "MB", "megabyte", "megabytes"), 12 | GIGA_BYTES(1_000_000_000, "GB", "gigabyte", "gigabytes"), 13 | TERA_BYTES(1_000_000_000, "TB", "terabyte", "terabytes"), 14 | PETA_BYTES(1_000_000_000_000L, "PB", "petabyte", "petabytes"), 15 | EXA_BYTES(1_000_000_000_000_000L, "EB", "exabyte", "exabytes"), 16 | ZETTA_BYTES(1_000_000_000_000_000_000L, "ZB", "zettabyte", "zettabytes"); 17 | 18 | final static Map sizeNameToSizeMap; 19 | 20 | static { 21 | 22 | final Map map = new HashMap<>(); 23 | Arrays.stream(MemorySizeUnit.values()).forEachOrdered(size -> 24 | size.getSizeNames() 25 | .forEach(sizeKey -> 26 | map.put(sizeKey, size) 27 | ) 28 | ); 29 | sizeNameToSizeMap = Collections.unmodifiableMap(map); 30 | } 31 | 32 | private final Set sizes; 33 | private final long multiplier; 34 | 35 | /** 36 | */ 37 | MemorySizeUnit(final long aMultiplier, final String... aSizes) { 38 | multiplier = aMultiplier; 39 | final LinkedHashSet strings = new LinkedHashSet<>(Arrays.asList(aSizes)); 40 | sizes = Collections.unmodifiableSet(strings); 41 | } 42 | 43 | public static long parse(String value) { 44 | return parseToConfigMemorySize(value).toBytes(); 45 | 46 | } 47 | 48 | 49 | public static ConfigMemorySize parseToConfigMemorySize(String value) { 50 | Objects.requireNonNull(value, "value Must not be null"); 51 | 52 | value = value.trim(); 53 | if (value.length() == 0) { 54 | throw new IllegalArgumentException("Value must not be null"); 55 | } 56 | 57 | int indexOfFirstNonNumber = -1; 58 | for (int index = 0; index < value.length(); index++) { 59 | final char c = value.charAt(index); 60 | if (!Character.isDigit(c)) { 61 | indexOfFirstNonNumber = index; 62 | break; 63 | } 64 | } 65 | 66 | final String sizeTypeStr = indexOfFirstNonNumber != -1 ? value.substring(indexOfFirstNonNumber) : "b"; 67 | final String numberString = value.substring(0, indexOfFirstNonNumber).trim(); 68 | final MemorySizeUnit sizeUnit = getSize(sizeTypeStr.length() > 0 ? sizeTypeStr : "b"); 69 | 70 | if (sizeUnit == null) { 71 | throw new IllegalArgumentException("Cannot parse size " + value); 72 | } 73 | 74 | return new ConfigMemorySize(sizeUnit, parseLong(numberString)); 75 | 76 | } 77 | 78 | private static MemorySizeUnit getSize(final String sizeTypeStr) { 79 | return sizeNameToSizeMap.get(sizeTypeStr.trim()); 80 | } 81 | 82 | public static ConfigMemorySize kilobytes(long units) { 83 | return new ConfigMemorySize(KILO_BYTES, units); 84 | } 85 | 86 | public static ConfigMemorySize bytes(long units) { 87 | return new ConfigMemorySize(BYTES, units); 88 | } 89 | 90 | public static ConfigMemorySize megabytes(long units) { 91 | return new ConfigMemorySize(MEGA_BYTES, units); 92 | } 93 | 94 | public static ConfigMemorySize gigabytes(long units) { 95 | return new ConfigMemorySize(GIGA_BYTES, units); 96 | } 97 | 98 | public Set getSizeNames() { 99 | return sizes; 100 | } 101 | 102 | public long getMultiplier() { 103 | return multiplier; 104 | } 105 | 106 | public long toLong(long units) { 107 | return this.multiplier * units; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/config/ResourceUtils.java: -------------------------------------------------------------------------------- 1 | package io.advantageous.config; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileNotFoundException; 6 | import java.io.InputStream; 7 | import java.net.URI; 8 | import java.net.URL; 9 | 10 | /** 11 | * Used by sub-projects to find resources. 12 | */ 13 | @SuppressWarnings("WeakerAccess") 14 | public class ResourceUtils { 15 | 16 | 17 | public static InputStream findResource(final String resourceName) { 18 | InputStream resourceAsStream = ConfigLoader.class.getClassLoader().getResourceAsStream(resourceName); 19 | if (resourceAsStream == null) { 20 | resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(resourceName); 21 | } 22 | 23 | if (resourceAsStream == null) { 24 | final URI uri = URI.create(resourceName); 25 | if (uri.getScheme().equals("file")) { 26 | try { 27 | final String path = uri.getPath(); 28 | resourceAsStream = new FileInputStream(new File(path)); 29 | } catch (FileNotFoundException e) { 30 | throw new IllegalArgumentException("File resource could not be loaded " + resourceName); 31 | } 32 | } else if (uri.getScheme().equals("http")) { 33 | try { 34 | URL url = uri.toURL(); 35 | resourceAsStream = url.openStream(); 36 | } catch (Exception e) { 37 | throw new IllegalArgumentException("Web resource could not be loaded " + resourceName); 38 | } 39 | } else if (uri.getScheme().equals("classpath")) { 40 | String path = uri.getSchemeSpecificPart(); 41 | if (path.startsWith("//")) { 42 | path = path.substring(2); 43 | } else if (path.startsWith("/")) { 44 | path = path.substring(1); 45 | } 46 | resourceAsStream = ConfigLoader.class.getClassLoader().getResourceAsStream(path); 47 | if (resourceAsStream == null) { 48 | resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(path); 49 | } 50 | } 51 | } 52 | 53 | if (resourceAsStream == null) { 54 | throw new IllegalArgumentException("resources could not be loaded " + resourceName); 55 | } 56 | 57 | return resourceAsStream; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/resources/jjs-config-utils.js: -------------------------------------------------------------------------------- 1 | var env = Java.type("java.lang.System").getenv; 2 | var uri = Java.type("java.net.URI").create; 3 | var system = Java.type("java.lang.System"); 4 | var duration = Java.type("java.time.Duration"); 5 | var loader = Java.type("io.advantageous.config.ConfigLoader"); 6 | 7 | function load(resources) { 8 | loader.load(resources); 9 | } 10 | 11 | function configs(resources) { 12 | loader.configWithFallbacks(resources); 13 | } 14 | 15 | var yes = true; 16 | var on = true; 17 | var no = false; 18 | var off = false; 19 | 20 | /** To store private vars. */ 21 | var konf = { 22 | osNameInternal: system.getProperty("os.name").toLowerCase(), 23 | memorySize: Java.type("io.advantageous.config.MemorySizeUnit") 24 | }; 25 | 26 | function kilobytes(units) { 27 | return konf.memorySize.kilobytes(units); 28 | } 29 | 30 | function megabytes(units) { 31 | return konf.memorySize.megabytes(units); 32 | } 33 | 34 | function bytes(units) { 35 | return konf.memorySize.bytes(units); 36 | } 37 | 38 | function gigabytes(units) { 39 | return konf.memorySize.gigabytes(units); 40 | } 41 | 42 | function dockerHostOrDefault(defaultHost) { 43 | var dockerHost = env("DOCKER_HOST"); 44 | return dockerHost ? uri(dockerHost).getHost() : defaultHost; 45 | } 46 | 47 | function seconds(unit) { 48 | return duration.ofSeconds(unit); 49 | } 50 | 51 | function minutes(unit) { 52 | return duration.ofMinutes(unit); 53 | } 54 | 55 | function hours(unit) { 56 | return duration.ofHours(unit); 57 | } 58 | 59 | function days(unit) { 60 | return duration.ofDays(unit); 61 | } 62 | 63 | function millis(unit) { 64 | return duration.ofMillis(unit); 65 | } 66 | 67 | function milliseconds(unit) { 68 | return duration.ofMillis(unit); 69 | } 70 | 71 | function sysProp(prop) { 72 | return system.getProperty(prop); 73 | } 74 | 75 | function sysPropOrDefault(prop, defaultValue) { 76 | return system.getProperty(prop, defaultValue.toString()); 77 | } 78 | 79 | function isWindowsOS() { 80 | return (konf.osNameInternal.indexOf("win") >= 0); 81 | } 82 | 83 | function isMacOS() { 84 | return (konf.osNameInternal.indexOf("mac") >= 0); 85 | } 86 | 87 | function isUnix() { 88 | var OS = konf.osNameInternal; 89 | return (OS.indexOf("nix") >= 0 || OS.indexOf("nux") >= 0 || OS.indexOf("aix") > 0 ); 90 | } 91 | 92 | function isLinux() { 93 | return konf.osNameInternal.indexOf("linux") >= 0; 94 | } 95 | 96 | function isSolaris() { 97 | return (konf.osNameInternal.indexOf("sunos") >= 0); 98 | } 99 | 100 | function shell(command) { 101 | return Java.type('io.advantageous.boon.Runner').run(command); 102 | } 103 | -------------------------------------------------------------------------------- /src/test/java/io/advantageous/config/ConfigImplTest.java: -------------------------------------------------------------------------------- 1 | package io.advantageous.config; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | import java.net.URI; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | import static io.advantageous.boon.core.Maps.map; 11 | import static java.util.Arrays.asList; 12 | import static org.junit.Assert.*; 13 | 14 | public class ConfigImplTest { 15 | 16 | private Config config; 17 | 18 | @Before 19 | public void setUp() throws Exception { 20 | 21 | final Map map = map("int1", 1, 22 | "float1", 1.0, 23 | "double1", 1.0, 24 | "long1", 1L, 25 | "string1", "rick", 26 | "stringList", asList("Foo", "Bar"), 27 | "configInner", map( 28 | "int2", 2, 29 | "float2", 2.0 30 | ), 31 | "uri", URI.create("http://localhost:8080/foo"), 32 | "myClass", "java.lang.Object", 33 | "myURI", "http://localhost:8080/foo", 34 | "employee", map("id", 123, "name", "Geoff"), 35 | "employees", asList( 36 | map("id", 123, "name", "Geoff"), 37 | map("id", 456, "name", "Rick"), 38 | map("id", 789, "name", "Paul") 39 | ), 40 | "floats", asList(1.0, 2.0, 3.0), 41 | "doubles", asList(1.0, 2.0, 3.0), 42 | "longs", asList(1.0, 2.0, 3.0), 43 | "ints", asList(1, 2, 3), 44 | "intsNull", asList(1, null, 3), 45 | "intsWrongType", asList(1, true, 3) 46 | ); 47 | config = new ConfigFromObject(map); 48 | } 49 | 50 | @Test(expected = IllegalArgumentException.class) 51 | public void wrongTypeInList() { 52 | assertEquals(asList(1, 2, 3), config.getIntList("intsWrongType")); 53 | 54 | } 55 | 56 | @Test(expected = IllegalArgumentException.class) 57 | public void listHasNull() { 58 | assertEquals(asList(1, 2, 3), config.getIntList("intsNull")); 59 | } 60 | 61 | @Test 62 | public void testNumberList() throws Exception { 63 | assertEquals(asList(1.0, 2.0, 3.0), config.getDoubleList("doubles")); 64 | assertEquals(asList(1.0f, 2.0f, 3.0f), config.getFloatList("floats")); 65 | assertEquals(asList(1, 2, 3), config.getIntList("ints")); 66 | assertEquals(asList(1L, 2L, 3L), config.getLongList("longs")); 67 | } 68 | 69 | @Test 70 | public void testSimple() throws Exception { 71 | assertEquals(Object.class, config.get("myClass", Class.class)); 72 | assertEquals(URI.create("http://localhost:8080/foo"), config.get("uri", URI.class)); 73 | assertEquals(URI.create("http://localhost:8080/foo"), config.get("myURI", URI.class)); 74 | assertEquals(1, config.getInt("int1")); 75 | assertEquals(asList("Foo", "Bar"), config.getStringList("stringList")); 76 | assertEquals("rick", config.getString("string1")); 77 | assertEquals(1.0, config.getDouble("double1"), 0.001); 78 | assertEquals(1L, config.getLong("long1")); 79 | assertEquals(1.0f, config.getFloat("float1"), 0.001); 80 | System.out.println(config.toString()); 81 | } 82 | 83 | @Test 84 | public void testReadClass() throws Exception { 85 | final Employee employee = config.get("employee", Employee.class); 86 | assertEquals("Geoff", employee.name); 87 | assertEquals("123", employee.id); 88 | } 89 | 90 | @Test 91 | public void testReadListOfClass() throws Exception { 92 | final List employees = config.getList("employees", Employee.class); 93 | assertEquals("Geoff", employees.get(0).name); 94 | assertEquals("123", employees.get(0).id); 95 | } 96 | 97 | @Test 98 | public void testReadListOfConfig() throws Exception { 99 | final List employees = config.getConfigList("employees"); 100 | assertEquals("Geoff", employees.get(0).getString("name")); 101 | assertEquals(123, employees.get(0).getInt("id")); 102 | } 103 | 104 | @Test 105 | public void testSimplePath() throws Exception { 106 | 107 | assertTrue(config.hasPath("configInner.int2")); 108 | assertFalse(config.hasPath("configInner.foo.bar")); 109 | assertEquals(2, config.getInt("configInner.int2")); 110 | assertEquals(2.0f, config.getFloat("configInner.float2"), 0.001); 111 | } 112 | 113 | @Test 114 | public void testGetConfig() throws Exception { 115 | final Config configInner = config.getConfig("configInner"); 116 | assertEquals(2, configInner.getInt("int2")); 117 | assertEquals(2.0f, configInner.getFloat("float2"), 0.001); 118 | } 119 | 120 | @Test 121 | public void testGetConfigConvertIntoPojo() throws Exception { 122 | final Config configInner = config.getConfig("employee"); 123 | final Employee employee = configInner.get("this", Employee.class); 124 | assertEquals("Geoff", employee.name); 125 | assertEquals("123", employee.id); 126 | } 127 | 128 | @Test 129 | public void testGetMap() throws Exception { 130 | final Map map = config.getMap("configInner"); 131 | assertEquals(2, (int) map.get("int2")); 132 | assertEquals(2.0f, (double) map.get("float2"), 0.001); 133 | } 134 | 135 | @Test(expected = java.lang.IllegalArgumentException.class) 136 | public void testNoPath() throws Exception { 137 | config.getInt("department.employees"); 138 | } 139 | 140 | @SuppressWarnings("unused") 141 | private static class Employee { 142 | private String id; 143 | private String name; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/test/java/io/advantageous/config/ConfigMemorySizeTest.java: -------------------------------------------------------------------------------- 1 | package io.advantageous.config; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertFalse; 6 | import static org.junit.Assert.assertTrue; 7 | 8 | public class ConfigMemorySizeTest { 9 | 10 | @Test 11 | public void equals() throws Exception { 12 | 13 | final ConfigMemorySize unit1 = ConfigMemorySize.valueOf(" 10 b "); 14 | final ConfigMemorySize unit2 = MemorySizeUnit.parseToConfigMemorySize(" 10 b "); 15 | final ConfigMemorySize unit3 = MemorySizeUnit.parseToConfigMemorySize(" 1 b "); 16 | 17 | assertTrue(unit1.equals(unit2)); 18 | assertFalse(unit1.equals(unit3)); 19 | } 20 | 21 | @Test 22 | public void hashCodeTest() throws Exception { 23 | 24 | 25 | final ConfigMemorySize unit1 = MemorySizeUnit.parseToConfigMemorySize(" 10 b "); 26 | final ConfigMemorySize unit2 = MemorySizeUnit.parseToConfigMemorySize(" 10 b "); 27 | 28 | 29 | assertTrue(unit1.hashCode() == unit2.hashCode()); 30 | } 31 | 32 | @Test 33 | public void toStringTest() throws Exception { 34 | 35 | 36 | final ConfigMemorySize unit1 = MemorySizeUnit.parseToConfigMemorySize(" 10 b "); 37 | final ConfigMemorySize unit2 = MemorySizeUnit.parseToConfigMemorySize(" 10 b "); 38 | 39 | assertTrue(unit1.toString().equals(unit2.toString())); 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /src/test/java/io/advantageous/config/JsConfigTest.java: -------------------------------------------------------------------------------- 1 | package io.advantageous.config; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import java.lang.reflect.Constructor; 8 | import java.lang.reflect.InvocationTargetException; 9 | import java.net.URI; 10 | import java.time.Duration; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | import static io.advantageous.config.ConfigLoader.*; 15 | import static java.util.Arrays.asList; 16 | import static org.junit.Assert.*; 17 | 18 | public class JsConfigTest { 19 | 20 | private Config config; 21 | 22 | @Before 23 | public void setUp() throws Exception { 24 | config = configs(config("test-config.js"), config("reference.js")); 25 | } 26 | 27 | @Test 28 | public void testSimple() throws Exception { 29 | 30 | assertEquals(URI.create("http://localhost:8080/foo"), config.get("uri", URI.class)); 31 | assertEquals(URI.create("http://localhost:8080/foo"), config.get("myURI", URI.class)); 32 | assertEquals(1, config.getInt("int1")); 33 | assertEquals(asList("Foo", "Bar"), config.getStringList("stringList")); 34 | assertEquals("rick", config.getString("string1")); 35 | assertEquals(Object.class, config.get("myClass", Class.class)); 36 | assertEquals(1.0, config.getDouble("double1"), 0.001); 37 | assertEquals(1L, config.getLong("long1")); 38 | assertEquals(1.0f, config.getFloat("float1"), 0.001); 39 | System.out.println(config.toString()); 40 | } 41 | 42 | @Test 43 | public void testReadClass() throws Exception { 44 | final Employee employee = config.get("employee", Employee.class); 45 | assertEquals("Geoff", employee.name); 46 | assertEquals("123", employee.id); 47 | } 48 | 49 | @Test 50 | public void testReadListOfClass() throws Exception { 51 | final List employees = config.getList("employees", Employee.class); 52 | assertEquals("Geoff", employees.get(0).name); 53 | assertEquals("123", employees.get(0).id); 54 | } 55 | 56 | @Test 57 | public void testReadListOfConfig() throws Exception { 58 | final List employees = config.getConfigList("employees"); 59 | assertEquals("Geoff", employees.get(0).getString("name")); 60 | assertEquals(123, employees.get(0).getInt("id")); 61 | } 62 | 63 | @Test 64 | public void testSimplePath() throws Exception { 65 | 66 | assertTrue(config.hasPath("configInner.int2")); 67 | assertFalse(config.hasPath("configInner.foo.bar")); 68 | assertEquals(2, config.getInt("configInner.int2")); 69 | assertEquals(2.0f, config.getFloat("configInner.float2"), 0.001); 70 | } 71 | 72 | @Test 73 | public void testGetConfig() throws Exception { 74 | final Config configInner = config.getConfig("configInner"); 75 | assertEquals(2, configInner.getInt("int2")); 76 | assertEquals(2.0f, configInner.getFloat("float2"), 0.001); 77 | } 78 | 79 | @Test 80 | public void testGetMap() throws Exception { 81 | final Map map = config.getMap("configInner"); 82 | assertEquals(2, (int) map.get("int2")); 83 | assertEquals(2.0f, (double) map.get("float2"), 0.001); 84 | } 85 | 86 | @Test(expected = java.lang.IllegalArgumentException.class) 87 | public void testNoPath() throws Exception { 88 | config.getInt("department.employees"); 89 | } 90 | 91 | @Test(expected = InvocationTargetException.class) 92 | public void testPrivateConstructor() throws Exception { 93 | Assert.assertEquals(0, ConfigLoader.class.getConstructors().length); 94 | Assert.assertEquals(1, ConfigLoader.class.getDeclaredConstructors().length); 95 | Constructor constructor = ConfigLoader.class.getDeclaredConstructor(); 96 | Assert.assertNotNull(constructor); 97 | Assert.assertFalse(constructor.isAccessible()); 98 | constructor.setAccessible(true); 99 | constructor.newInstance(); 100 | } 101 | 102 | @Test 103 | public void testNumberList() throws Exception { 104 | assertEquals(asList(1.0, 2.0, 3.0), config.getDoubleList("doubles")); 105 | assertEquals(asList(1.0f, 2.0f, 3.0f), config.getFloatList("floats")); 106 | assertEquals(asList(1, 2, 3), config.getIntList("ints")); 107 | assertEquals(asList(1L, 2L, 3L), config.getLongList("longs")); 108 | } 109 | 110 | 111 | @Test 112 | public void testLoadConfig() throws Exception { 113 | Config config = load("test-config.js"); 114 | URI uri = config.get("myUri", URI.class); 115 | Assert.assertNotNull(uri); 116 | Assert.assertEquals("host", uri.getHost()); 117 | Map myMap = config.getMap("someKey"); 118 | Assert.assertNotNull(myMap); 119 | Assert.assertEquals(234, myMap.get("nestedKey")); 120 | } 121 | 122 | @Test(expected = IllegalArgumentException.class) 123 | public void testBadJs() throws Exception { 124 | load("bad-config.js"); 125 | } 126 | 127 | @Test(expected = IllegalArgumentException.class) 128 | public void wrongTypeInList() { 129 | assertEquals(asList(1, 2, 3), config.getIntList("intsWrongType")); 130 | 131 | } 132 | 133 | @Test(expected = IllegalArgumentException.class) 134 | public void listHasNull() { 135 | assertEquals(asList(1, 2, 3), config.getIntList("intsNull")); 136 | } 137 | 138 | @Test 139 | public void testDuration() { 140 | assertEquals(Duration.ofMillis(10), config.getDuration("tenMillis")); 141 | assertEquals(Duration.ofMillis(10), config.getDuration("tenMilliseconds")); 142 | assertEquals(Duration.ofSeconds(10), config.getDuration("tenSeconds")); 143 | assertEquals(Duration.ofMinutes(10), config.getDuration("tenMinutes")); 144 | assertEquals(Duration.ofHours(10), config.getDuration("tenHours")); 145 | assertEquals(Duration.ofDays(10), config.getDuration("tenDays")); 146 | 147 | assertEquals(Duration.ofMinutes(15), config.getDuration("fifteenMinutes")); 148 | 149 | assertEquals(Duration.ofSeconds(10), config.getDuration("tenSeconds2")); 150 | assertEquals(Duration.ofMinutes(10), config.getDuration("tenMinutes2")); 151 | assertEquals(Duration.ofHours(10), config.getDuration("tenHours2")); 152 | assertEquals(Duration.ofDays(10), config.getDuration("tenDays2")); 153 | assertEquals(Duration.ofMillis(10), config.getDuration("tenMillis2")); 154 | assertEquals(Duration.ofMillis(10), config.getDuration("tenMilliseconds2")); 155 | assertEquals(asList(Duration.ofSeconds(10)), config.getDurationList("durationList")); 156 | 157 | 158 | } 159 | 160 | @Test 161 | public void testBoolean() { 162 | 163 | /* Just make sure it does not throw an exception. */ 164 | config.getBoolean("macOS"); 165 | config.getBoolean("windows"); 166 | 167 | } 168 | 169 | 170 | @Test 171 | public void testParseInt() { 172 | 173 | /* Just make sure it does not throw an exception. */ 174 | final int parseInt = config.getInt("parseInt"); 175 | assertEquals(123, parseInt); 176 | 177 | } 178 | 179 | @Test 180 | public void testStringNumber() { 181 | 182 | /* Just make sure it does not throw an exception. */ 183 | final int parseInt = config.getInt("stringNumber"); 184 | assertEquals(123, parseInt); 185 | 186 | } 187 | 188 | @Test 189 | public void testGetURIFromString() { 190 | 191 | final URI uri = config.getUri("uriFromString"); 192 | assertNotNull(uri); 193 | assertEquals("http", uri.getScheme()); 194 | } 195 | 196 | 197 | @Test 198 | public void testGetURIFromURI() { 199 | 200 | final URI uri = config.getUri("uriFromURI"); 201 | assertNotNull(uri); 202 | assertEquals("http", uri.getScheme()); 203 | } 204 | 205 | @Test 206 | public void testBooleanList() { 207 | 208 | final List booleanList = config.getBooleanList("booleanList"); 209 | assertNotNull(booleanList); 210 | assertTrue(booleanList.get(0)); 211 | assertFalse(booleanList.get(1)); 212 | } 213 | 214 | 215 | @Test 216 | public void testURIList() { 217 | 218 | final List uriList = config.getUriList("uriList"); 219 | assertNotNull(uriList); 220 | assertEquals(8080, uriList.get(0).getPort()); 221 | assertEquals(8082, uriList.get(2).getPort()); 222 | } 223 | 224 | @Test(expected = IllegalArgumentException.class) 225 | public void testBadUri() { 226 | 227 | config.getUri("badUri"); 228 | } 229 | 230 | 231 | @Test(expected = IllegalArgumentException.class) 232 | public void testBadUri2() { 233 | 234 | config.getUri("badUri2"); 235 | } 236 | 237 | @Test(expected = IllegalArgumentException.class) 238 | public void badInt() { 239 | 240 | final Integer badInt = config.getInt("badInt"); 241 | System.out.println("BAD INT " + badInt); 242 | } 243 | 244 | @Test(expected = IllegalArgumentException.class) 245 | public void badBoolean() { 246 | 247 | config.getBoolean("badBoolean"); 248 | } 249 | 250 | @Test(expected = IllegalArgumentException.class) 251 | public void badBoolean2() { 252 | 253 | config.getBoolean("badBoolean2"); 254 | } 255 | 256 | 257 | @Test(expected = IllegalArgumentException.class) 258 | public void badDuration() { 259 | 260 | config.getDuration("badDuration"); 261 | } 262 | 263 | @Test(expected = IllegalArgumentException.class) 264 | public void badDuration2() { 265 | 266 | config.getDuration("badDuration2"); 267 | } 268 | 269 | @Test(expected = IllegalArgumentException.class) 270 | public void badDuration3() { 271 | 272 | config.getDuration("badDuration3"); 273 | } 274 | 275 | @Test 276 | public void duraitonMillis() { 277 | 278 | final Duration durationMillis = config.getDuration("durationMillis"); 279 | assertEquals(Duration.ofMillis(123), durationMillis); 280 | } 281 | 282 | 283 | @Test 284 | public void memorySize() { 285 | 286 | final ConfigMemorySize diskSpace = config.getMemorySize("diskSpace"); 287 | assertEquals(ConfigMemorySize.valueOf("10GB"), diskSpace); 288 | } 289 | 290 | 291 | @Test 292 | public void memorySizes() { 293 | final List diskVolumes = config.getMemorySizeList("diskVolumes"); 294 | assertEquals(ConfigMemorySize.valueOf("10GB"), diskVolumes.get(0)); 295 | assertEquals(ConfigMemorySize.valueOf("10GB"), diskVolumes.get(1)); 296 | assertEquals(ConfigMemorySize.valueOf("10GB"), diskVolumes.get(2)); 297 | assertEquals(ConfigMemorySize.valueOf("10B"), diskVolumes.get(3)); 298 | } 299 | 300 | @Test 301 | public void fallback() { 302 | final String value = config.getString("abc"); 303 | assertEquals("abc", value); 304 | 305 | final String value1 = config.getString("def"); 306 | assertEquals("def", value1); 307 | } 308 | 309 | // 310 | @SuppressWarnings("unused") 311 | private static class Employee { 312 | private String id; 313 | private String name; 314 | } 315 | 316 | @Test 317 | public void testRunCommand() { 318 | assertNotNull(config.getString("ls")); 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /src/test/java/io/advantageous/config/JsLoadTest.java: -------------------------------------------------------------------------------- 1 | package io.advantageous.config; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.File; 6 | import java.net.URI; 7 | 8 | import static java.util.Arrays.asList; 9 | import static org.junit.Assert.assertEquals; 10 | import static org.junit.Assert.assertTrue; 11 | 12 | public class JsLoadTest { 13 | 14 | @Test(expected = IllegalArgumentException.class) 15 | public void loadDontExist() { 16 | ConfigLoader.load("crapthatdontexist"); 17 | } 18 | 19 | @Test 20 | public void loadClasspathURI() { 21 | final Config config = ConfigLoader.load("classpath:test-config.js"); 22 | assertEquals(asList(1.0, 2.0, 3.0), config.getDoubleList("doubles")); 23 | } 24 | 25 | 26 | @Test 27 | public void loadClasspathURI2() { 28 | final Config config = ConfigLoader.load("classpath:/test-config.js"); 29 | assertEquals(asList(1.0, 2.0, 3.0), config.getDoubleList("doubles")); 30 | } 31 | 32 | 33 | @Test 34 | public void loadClasspathURI3() { 35 | final Config config = ConfigLoader.load("classpath://test-config.js"); 36 | assertEquals(asList(1.0, 2.0, 3.0), config.getDoubleList("doubles")); 37 | } 38 | 39 | @Test 40 | public void loadAsClassResources() { 41 | final Config config = ConfigLoader.load("test-config.js"); 42 | assertEquals(asList(1.0, 2.0, 3.0), config.getDoubleList("doubles")); 43 | } 44 | 45 | 46 | @Test 47 | public void testFile() { 48 | File file = new File("./src/test/resources/test-config.js"); 49 | assertTrue(file.exists()); 50 | System.out.println(file.toURI().toString()); 51 | final Config config = ConfigLoader.load(file.toURI().toString()); 52 | assertEquals(asList(1.0, 2.0, 3.0), config.getDoubleList("doubles")); 53 | } 54 | 55 | 56 | @Test 57 | public void testFile2() { 58 | File file = new File("./src/test/resources/test-config.js"); 59 | file = file.getAbsoluteFile(); 60 | final URI uri = URI.create("file://" + file.toString()); 61 | System.out.println(uri); 62 | final Config config = ConfigLoader.load(uri.toString()); 63 | assertEquals(asList(1.0, 2.0, 3.0), config.getDoubleList("doubles")); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/io/advantageous/config/MemorySizeUnitTest.java: -------------------------------------------------------------------------------- 1 | package io.advantageous.config; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | /** 8 | * Created by rick on 5/1/16. 9 | */ 10 | public class MemorySizeUnitTest { 11 | @Test 12 | public void parse() throws Exception { 13 | 14 | assertEquals(10, MemorySizeUnit.parse("10 b")); 15 | assertEquals(10, MemorySizeUnit.parse("10bytes")); 16 | assertEquals(10, MemorySizeUnit.parse("10byte")); 17 | assertEquals(10_000_000, MemorySizeUnit.parse("10 MB")); 18 | assertEquals(10_000_000, MemorySizeUnit.parse("10megabyte")); 19 | assertEquals(10_000_000, MemorySizeUnit.parse("10 megabytes ")); 20 | assertEquals(10_000_000_000L, MemorySizeUnit.parse(" 10 gigabytes ")); 21 | assertEquals(10_000_000_000L, MemorySizeUnit.parse(" 10 GB ")); 22 | assertEquals(10_000_000_000L, MemorySizeUnit.parse("10gigabyte")); 23 | assertEquals(MemorySizeUnit.ZETTA_BYTES.getMultiplier(), MemorySizeUnit.parse(" 1 zettabytes ")); 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /src/test/resources/bad-config.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file intentionally incomplete JavaScript. It is used to test error handling. 3 | */ 4 | var config = { 5 | potato ? 6 | -------------------------------------------------------------------------------- /src/test/resources/reference.js: -------------------------------------------------------------------------------- 1 | var config = { 2 | abc: "abcFallback", 3 | def: "def" 4 | } -------------------------------------------------------------------------------- /src/test/resources/test-config.js: -------------------------------------------------------------------------------- 1 | var config = { 2 | 3 | abc: "abc", 4 | 5 | myUri: uri("http://host:9000/path?foo=bar"), 6 | 7 | someKey: { 8 | nestedKey: 234, 9 | other: "this text" 10 | }, 11 | 12 | int1: 1, 13 | float1: 1.0, 14 | double1: 1.0, 15 | long1: 1, 16 | string1: "rick", 17 | stringList: ['Foo', 'Bar'], 18 | configInner: { 19 | int2: 2, 20 | float2: 2.0 21 | }, 22 | uri: uri("http://localhost:8080/foo"), 23 | myClass: "java.lang.Object", 24 | myURI: "http://localhost:8080/foo", 25 | employee: {"id": 123, "name": "Geoff"}, 26 | employees: [ 27 | {id: 123, "name": "Geoff"}, 28 | {id: 456, "name": "Rick"}, 29 | {id: 789, 'name': "Paul"} 30 | ], 31 | floats: [1.0, 2.0, 3.0], 32 | doubles: [1.0, 2.0, 3.0], 33 | longs: [1.0, 2.0, 3.0], 34 | ints: [1, 2, 3], 35 | intsNull: [1, null, 3], 36 | intsWrongType: [1, "2", 3], 37 | tenSeconds: seconds(10), 38 | tenDays: days(10), 39 | tenMinutes: minutes(10), 40 | tenHours: hours(10), 41 | tenMillis: millis(10), 42 | tenMilliseconds: milliseconds(10), 43 | fifteenMinutes: "PT15M", 44 | tenSeconds2: "10 seconds", 45 | tenMinutes2: "10m", 46 | tenHours2: "10 h", 47 | tenDays2: "10 day", 48 | tenMillis2: "10ms", 49 | tenMilliseconds2: milliseconds(10), 50 | durationList: [seconds(10)], 51 | macOS: isMacOS(), 52 | windows: isWindowsOS(), 53 | linux: isLinux(), 54 | unix: isUnix(), 55 | solaris: isSolaris(), 56 | parseInt: parseInt("123"), 57 | stringNumber: "123", 58 | uriFromString: "http://localhost:8080/", 59 | uriFromURI: uri("http://localhost:8080/"), 60 | booleanList: [true, false, "yes", "no", "on", "off", yes, no, off, on], 61 | uriList: ["http://localhost:8080/", "http://localhost:8081/", 62 | uri("http://localhost:8082/")], 63 | badUri: 1, 64 | badUri2: "fffff::::\u0000::::::::::::::::::a1", 65 | badBoolean: "crap", 66 | badBoolean2: 1, 67 | badInt: "crap", 68 | badInt2: yes, 69 | badDuration: "crap", 70 | badDuration2: "abc seconds", 71 | durationMillis: 123, 72 | diskSpace: " 10 gigabytes", 73 | diskVolumes: [" 10 gigabytes", "10GB", "10 gigabytes", 10, kilobytes(10), megabytes(10), bytes(10), gigabytes(10)], 74 | 75 | ls: shell('ls -l') 76 | }; 77 | --------------------------------------------------------------------------------