-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Typesafe Config is one of the more popular configuration libraries for JVM languages. Unfortunately it doesn't offer a convenient way to handle optional properties.
The authors of Typesafe Config take an opinionated view on the handling of default properties:
Many other configuration APIs allow you to provide a default to the getter methods, like this: boolean getBoolean(String path, boolean fallback) Here, if the path has no setting, the fallback would be returned. An API could also return null for unset values, so you would check for null: // returns null on unset, check for null and fall back Boolean getBoolean(String path) The methods on the Config interface do NOT do this, for two major reasons: 1. If you use a config setting in two places, the default fallback value gets cut-and-pasted and typically out of sync. This can result in Very Evil Bugs. 2. If the getter returns null (or None, in Scala) then every time you get a setting you have to write handling code for null/None and that code will almost always just throw an exception. Perhaps more commonly, people forget to check for null at all, so missing settings result in NullPointerException.
While this is generally applicable to default values, it does not address scenarios where properties have no defaults and are inherently optional.
For instance, consider a directory copy utility capable of filtering on file extensions. The utility would be expected to transfer all files indiscriminately if no file extensions are specified. Without native capability to handle optional properties, developers would have to resort to writing cumbersome boilerplate code or use a special placeholder value to indicate no filter.
See issues 186, 282, 286, 110, 440 for historical discussion.
Typesafe Config authors do offer some suggestions for dealing with optional properties:
If you want to allow a setting to be missing from `application.conf` in a particular case, then here are some options: 1. Set it in a `reference.conf` included in your library or application jar, so there's a default value. 2. Use the `Config.hasPath()` method to check in advance whether the path exists (rather than checking for `null`/`None` after as you might in other APIs). 3. Catch and handle `ConfigException.Missing`. NOTE: using an exception for control flow like this is much slower than using `Config.hasPath()`; the JVM has to do a lot of work to throw an exception. 4. In your initialization code, generate a `Config` with your defaults in it (using something like `ConfigFactory.parseMap()`) then fold that default config into your loaded config using `withFallback()`, and use the combined config in your program. "Inlining" your reference config in the code like this is probably less convenient than using a `reference.conf` file, but there may be reasons to do it. 5. Use `Config.root()` to get the `ConfigObject` for the `Config`; `ConfigObject` implements `java.util.Map` and the `get()` method on `Map` returns null for missing keys. See the API docs for more detail on `Config` vs. `ConfigObject`. 6. Set the setting to `null` in `reference.conf`, then use `Config.getIsNull` and `Config.hasPathOrNull` to handle `null` in a special way while still throwing an exception if the setting is entirely absent.
However, none of these options provide the functionality to support optional properties as a top-tier feature.
This project is a companion to Typesafe Config, designed to allow users a more convenient and elegant way to handle optional properties.
- Java 8 or higher is required.
The table below compares and contrasts this class to the Config class from Typesafe Config:
Method | Property value |
Config result |
Strict mode |
OptionalConfig result |
---|---|---|---|---|
getXXXX | Valid value | Returns the property value | On/Off | Returns the property value |
getXXXX | Null value | Exception | Off | Returns null |
getXXXX | Null value | Exception | On | Exception |
getXXXX | Invalid value | Exception | On/Off | Exception |
getXXXX | Missing property | Exception | On/Off | Exception |
getXXXX | Missing value | Exception | On/Off | Exception |
getXXXXList | Valid list (no null values) | Returns the list of values | On/Off | Returns the list of values |
getXXXXList | Valid list (contains null values) | Exception | Off | Returns the list of values (including nulls) |
getXXXXList | Valid list (contains null values) | Exception | On | Exception |
getXXXXList | Null list | Exception | Off | Returns null |
getXXXXList | Null list | Exception | On | Exception |
getXXXXList | Invalid list | Exception | On/Off | Exception |
getXXXXList | Missing property | Exception | On/Off | Exception |
getXXXXList | Missing value | Exception | On/Off | Exception |
getOptionalXXXX | Valid value | N/A | On/Off | Returns an Optional containing the value |
getOptionalXXXX | Null value | N/A | On/Off | Returns Optional.empty()
|
getOptionalXXXX | Missing property | N/A | On/Off | Returns Optional.empty()
|
getOptionalXXXX | Invalid value | N/A | On/Off | Exception |
getOptionalXXXX | Missing value | N/A | On/Off | Exception |
getOptionalXXXXList | Valid list (no null values) | N/A | On/Off | Returns an Optional containing the list of values |
getOptionalXXXXList | Valid list (contains null values) | N/A | Off | Returns an Optional containing the list of values (including nulls) |
getOptionalXXXXList | Valid list (contains null values) | N/A | On | Exception |
getOptionalXXXXList | Null list | N/A | On/Off | Returns Optional.empty()
|
getOptionalXXXXList | Missing property | N/A | On/Off | Returns Optional.empty()
|
getOptionalXXXXList | Invalid list | N/A | On/Off | Exception |
getOptionalXXXXList | Missing value | N/A | On/Off | Exception |
Note that the getEnumList
method is re-implemented as getEnumSet
by Optional Config.
Method | Functionality |
---|---|
getShort getOptionalShort getShortList getOptionalShortList |
Offering methods which operate on properties referencing Short values (for completeness). These methods behave similarly to their Integer and Long counterparts. |
getPath getOptionalPath getPathList getOptionalPathList |
Offering methods which operate on properties referencing Path values. A valid java.nio.file.Path may refer to an existing file or a directory or may not exist on the filesystem. |
getFile getOptionalFile getFileList getOptionalFileList |
Offering methods which operate on properties referencing existing files. These methods will throw exceptions if the referenced files do not exist on the filesystem. |
getDirectory getOptionalDirectory getDirectoryList getOptionalDirectoryList |
Offering methods which operate on properties referencing existing directories. These methods will throw exceptions if the referenced directories do not exist on the filesystem. |
at | Offering the capability to view nested configuration objects as a standalone OptionalConfig . |