Flexible and easy to use config library written in kotlin

Overview

Build Status Coverage Status License Download

Overview

Cfg4k is a configuration library made for Kotlin in Kotlin!

Features

  • Automatic reload
  • Interface binding
  • Ability to handle data classes automatically
  • All the complex types and generics are supported
  • Huge flexibility, custom sources
  • Easy to use
  • Bytebuddy provider will be able to compile your bindings at runtime (You will need to add the cfg4k-bytebuddy to your dependencies.)

For further information, use the wiki

Quick start

  1. Add the Bintray repository:
repositories {
    jcenter()
}
  1. Add the dependency for the module(s) that you are going to use
compile 'com.jdiazcano.cfg4k:cfg4k-core:$VERSION'
fun main(args: Array<String>) {
    val source = ClassPathConfigSource("global.properties")         // Create source
    val loader = PropertyConfigLoader(source)                       // Create loader
    val provider = ProxyConfigProvider(loader)                      // Create provider
    val databaseConfig = provider.bind<DatabaseConfig>("database")  // bind and use

    println("Name: ${databaseConfig.name()}")
    println("Url: ${databaseConfig.url()}")
    println("Port: ${databaseConfig.port()}")
}

/**
 * This interface defines a database configuration
 */
interface DatabaseConfig {
    /**
     * You can have javadocs inside your properties and this is really cool
     */
    fun url(): String
    fun port(): Int
    fun name(): String

    // if you have an unused property you know it and you can delete it
    val unused: String

    @Deprecated("You can even deprecate properties!")
    fun deprecated(): Boolean
    
    val youCanuseValuesToo: String
    val andNullables: Int?
}

Architeture overview

Lightbox

License

Licensed under the Apache License, Version 2.0. See LICENSE file.

Comments
  • Support environment variables

    Support environment variables

    Support binding environment variables to configuration (12-factor app compatible).

    Spring Boot does a particularly good job at this, by mapping properties defined internally to corresponding idiomatic environment variable syntax e.g. foo.bar.baz -> FOO_BAR_BAZ. They also support some flexibility in the mapping (relaxed binding).

    Cfg4j on the other hand does a poor job of this, unless its improved since the last time I looked at cfg4j.

    enhancement 
    opened by rocketraman 19
  • Using of EnvironmentConfigLoader based provider requires all the intermediate 'nodes' to be declared

    Using of EnvironmentConfigLoader based provider requires all the intermediate 'nodes' to be declared

    Hello! Firstly I used an OverrideConfigProvider combining two proxy config providers based on both an environment config loader and a property config loader (from the properties file) and everything worked well. Then I decided to refactor the code to be able to load configs from the environment with an opportunity to use a secondary properties source if the config is not consistent after the 'first' load. I began with validating the loaded config structure but failed because I thought only about the reflection API possibilities. (Would appreciate a piece of advice if a one can perform the consistency validation somehow). So I managed to change the solution to a worse one - I just return 'environment provider' instead of mixed one in case of a file reading failure.

    And here comes the main question. So if I try to load a config using ONLY environment variables there must be contained each 'intermediate node'. So if I have the following picture in my environment ('properties' is a prefix):

    PROPERTIES_GROUPONE_KEYONE -> "VALUEONE"
    

    I just get an Exception: com.jdiazcano.cfg4k.utils.SettingNotFound: Setting 'properties.groupone' not found. on an attempt to get the value. But if I add:

    PROPERTIES_GROUPONE -> ""
    PROPERTIES_GROUPONE_KEYONE -> "VALUEONE"
    PROPERTIES_GROUPONE_KEYTWO -> "VALUETWO"
    

    everything gets working. Is this behavior incorrect? I do not need to include in my properties file each 'intermediate node' though. But I do in case of working with the environment.

    Thank you!

    bug 
    opened by Mingela 14
  • ClassNotFoundException on $DefaultImpls

    ClassNotFoundException on $DefaultImpls

    Hi, Love the concept of this library and that it appears to be actively maintained.

    I'm trying to use it in a project, and running into an issue where it seems that it relies on interfaces having a $DefaultImpls which as far as I can tell will not be the case in the basic use cases listed in e.g. your examples?

    I end up with a ClassNotFoundException on Config$DefaultImpls in https://github.com/jdiazcano/cfg4k/blob/master/cfg4k-core/src/main/kotlin/com/jdiazcano/cfg4k/binders/BindingInvocationHandler.kt#L63 when using the following code: Config.kt:

    enum class Environment {
        DEVELOPMENT,
        STAGING,
        PRODUCTION;
    
        fun slug() = name.toLowerCase()
    }
    
    interface Config {
        val database: DatabaseConfig
        val server: ServerConfig
    }
    
    interface DatabaseConfig {
        val url: String
        val username: String
        val password: String
    }
    
    interface ServerConfig {
        val port: Int
        val host: String
        val environment: Environment
    }
    

    App.kt (redacted):

            val sysLoader = SystemPropertyConfigLoader()
            val envLoader = EnvironmentConfigLoader()
            val baseProvider = OverrideConfigProvider(
                DefaultConfigProvider(sysLoader),
                DefaultConfigProvider(envLoader)
            )
            val baseConfig = baseProvider.bind<Config>()
    
            val propsDefaultLoader = PropertyConfigLoader(ClasspathConfigSource("/app.properties"))
            val propsEnvLoader = PropertyConfigLoader(ClasspathConfigSource("/app.${baseConfig.server.environment}.properties"))
            val provider = OverrideConfigProvider(
                DefaultConfigProvider(sysLoader),
                DefaultConfigProvider(envLoader),
                DefaultConfigProvider(propsDefaultLoader),
                DefaultConfigProvider(propsEnvLoader)
            )
            val config = provider.bind<Config>()
    
            print(config.server.host)
            print(config.database.url)
    

    Am I missing something obvious? If I make a stubbed default function on the objects I can get past the first error (missing $DefaultImpls) but it doesn't get me much further as it still seems to expect Kotlin to auto-stub all of these, which isn't happening for me.

    Thanks in advance for your help!

    bug 
    opened by cojo 10
  • ByteBuddyBinder creates a new class on every method invocation

    ByteBuddyBinder creates a new class on every method invocation

    When using the ByteBuddy binder, every method invocation to a config object causes a new class to be created and loaded into memory. Instead, the binder should create the class only once, with the methods in the class reading the provider every time they are executed.

    This is easy to reproduce. Create any config binding with ByteBuddy. Read a method on the config in a tight loop. Start the JVM with -verbose:class to see the problem.

    bug 
    opened by rocketraman 6
  • [BUG] Unexpected crash in DefaultConfigLoader.get with IllegalArgumentException: Trying to get a key from a primitive

    [BUG] Unexpected crash in DefaultConfigLoader.get with IllegalArgumentException: Trying to get a key from a primitive

    Describe the bug Since upgrading to com.jdiazcano.cfg4k:cfg4k-core:0.9.41 i am getting the following crash

    Exception in thread "main" java.lang.IllegalArgumentException: Trying to get a key from a primitive at com.jdiazcano.cfg4k.loaders.DefaultConfigLoader.get(DefaultConfigLoader.kt:27) at com.jdiazcano.cfg4k.providers.DefaultConfigProvider.get(DefaultConfigProvider.kt:51) at com.jdiazcano.cfg4k.providers.CachedConfigProvider.get(CachedConfigProvider.kt:37) at com.abc.config.ApplicationConfig.getProperty(ApplicationConfig.kt:24) at com.abc.repository.util.DataSource.(DataSource.kt:26)

    To Reproduce In order for me to reproduce this behavior I need:

    1. Version of Cfg4k - com.jdiazcano.cfg4k:cfg4k-core:0.9.41
    2. ConfigProvider - CachedConfigProvider
    3. ConfigLoader - EnvironmentConfigLoader
    4. ConfigSource - Environment Variables
    5. Configuration files unless the source is a String - Environment Variables
    6. Any interfaces/dataclasses used for binding/getting - nope

    As example, it is easy to get an easy prototype with all the 5 points in the list: I have the following configs for a DB.

            val url = Property("db.conn", "")
            val driverName = Property("db.driver.name", "org.postgresql.Driver")
            val userName = Property("db.username", "")
            val password = Property("db.password", "")
            val connectionTimeout = Property("db.conn.timeout.sec", TimeUnit.SECONDS.toMillis(30))
            val idleTimeout = Property("db.conn.idle.timeout.sec", TimeUnit.MINUTES.toMillis(10))
            val maxLifetime = Property("db.conn.max.lifetime.sec", TimeUnit.MINUTES.toMillis(30))
            val minimumIdle = Property("db.conn.minimum.idle", 4)
            val maximumPoolSize = Property("db.conn.maximum.pool.size", 32)
            val connectionTestQuery = Property("db.conn.test.query", "SELECT 1")
    
    
    data class Property<T : Any>(
        val key: String,
        val default: T
    )
    I use cfg4k like the following:
     override fun <T : Any> getProperty(prop: Property<T>): T {
    
            return configProvider.get(prop.key, prop.default.javaClass, prop.default)
        }
    

    It seems like when i am searching for val connectionTimeout = Property("db.conn.timeout.sec", TimeUnit.SECONDS.toMillis(30)) This value does not exist in my Environment Variables, and should return the default. it may be getting confused with val url = Property("db.conn", "") Which does exist in the Environment Variables. Since it finds ConfigObject(value=jdbc:postgresql://127.0.0.1:1234/abc) as root and then fails.

    This works on 0.9.3

    Before the key in fun DefaultConfigLoader.get() was different key = DB_CONN_TIMEOUT_SEC - 0.9.3 key = DB.CONN.TIMEOUT.SEC - 0.9.41 Before it wasnt split and now it is.

    Expected behavior Since val connectionTimeout = Property("db.conn.timeout.sec", TimeUnit.SECONDS.toMillis(30)) does not exist it should return the default value.

    Additional context Let me know if i am not using this Library correctly.

    opened by credmond-git 5
  • Authentication for URLConfigSource

    Authentication for URLConfigSource

    Current URLConfigSource lacks authentication, so the URL can only be public to be consumed by cfg4k (unless you tweak the global java.net.Authenticator which seems not the best way).

    This change makes URLConfigSource to accept an optional generic content for Authorization header to be used when fetching the config from the provided URL. A helper function for encoding basic auth credentials is provided along the way just because it's easy on one hand but not completely trivial on the other.

    opened by detouched 5
  • Map of Strings?

    Map of Strings?

    Is it possible to take a property with a Map<String, String>/Properties? I've tried creating parsers etc, but I couldn't get it to use my parser, and it seems the BindingInvocationHandler just throws SettingNotFound when trying to use any of the maps methods.

    I even tried just creating a property that was a ConfigObject type and turn it into a map myself, but it threw a SettingNotFound: Setting 'asObject' not found.

    While I know most of my config and its types beforehand and can add them to the interface, I'd like to be able to supply dynamic/unknown pairs of config through. Is this possible?

    opened by tellisnz-shift 4
  • Make TimedReloadStrategy error-prone and reusable

    Make TimedReloadStrategy error-prone and reusable

    Current TimedReloadStrategy has two flaws:

    • the underlying Timer terminates if any reload attempt fails with an exception. This makes this reload strategy suck when dealing with I/O, for instance, reloading from a remote URL.
    • if register(ConfigProvider) is called multiple times, only the last ConfigProvider can effectively call deregister(), while all previously registered timers will be lost. This makes the reload strategy not reusable, while its API suggests that it might be reused.

    This change makes TimedReloadStrategy use a scheduled executor service with error-prone reload() call to prevent subsequent reload attempts from auto-cancelling, and use a map of ConfigProviders to register and unregister them properly.

    opened by detouched 3
  • FileChangeReloadStrategy thread dies on Exception: no error handler

    FileChangeReloadStrategy thread dies on Exception: no error handler

    The FileChangeReloadStrategy has no exception handler. This means that if an exception is thrown when the underlying config is updated -- for example, if the config is updated with invalid syntax -- the reload thread dies and no more config reloads are handled until the ConfigProvider is constructed again.

    Instead, I think I would log a warning in this situation, but continue to monitor the file for more changes. However, I also note that there is no logging in the lib now, so an alternate approach may be to trigger another listener on provider in this case e.g. addReloadErrorListener?

    enhancement 
    opened by rocketraman 3
  • FileChangeReloadStrategy watcher uses incorrect test

    FileChangeReloadStrategy watcher uses incorrect test

    The FileChangeReloadStrategy watcher uses an incorrect test to check if the event corresponds to the configuration file.

    The javadocs for WatchEvent.context state that the path returned is relative to the parent on which the watch is registered.

    However, the current code just naively does fileName == file. The following code seems to be a pretty foolproof way of doing the correct test:

    parent.resolve(fileName).toRealPath() == file.toRealPath()

    opened by rocketraman 3
  • Library versions with known CVEs

    Library versions with known CVEs

    There are libraries with known CVEs in the published version of cfg4k -- mainly the s3 dependency brings in older versions of jackson and httpclient, and the jgit dependency also brings in an older version of httpclient.

    This is fixed on my branch here: https://github.com/rocketraman/cfg4k/tree/library-updates-security.

    opened by rocketraman 2
  • Reloading when merging

    Reloading when merging

    Description

    Right now if you merge two ConfigLoaders there is no way to keep the reload. An empty reload will take its place.

    One thing that could be done is that the reload is a function in the constructor or something that can also be merged/called one after the other, this needs a little bit of design!

    Acceptance criteria

    • Document the fix in this issue
    • Code changes and tests
    opened by jdiazcano 0
  • Spurious WARN at startup on Oracle JDK

    Spurious WARN at startup on Oracle JDK

    Minor issue with cfg4k 0.9.0: always get this WARN level message from PropertyConfigLoader at startup, using Oracle JDK 1.8:

    Key (java.vendor.url.bug=Oracle Corporation) has overridden another value used as root
    
    opened by rocketraman 1
  • JDBC config loader?

    JDBC config loader?

    Maybe in some way it would be nice to have a jdbc config loader to be able to use it with all the databases.

    This has the inconvenience of nested properties.

    a.b could be property B from table A but... what about a.b.c ?

    Another thing is that it could have a properties table (defined when creating the loader) and the key would be a.b or a.b.c

    idea to do 
    opened by jdiazcano 0
  • Notify which properties changed

    Notify which properties changed

    When reloading, the list of configuration changes should be listed so whomever is reloading can act to that. (Even if it shouldn't be needed because it shouldn't be used as static fields but with the binding)

    idea 
    opened by jdiazcano 0
Releases(0.9.5)
  • 0.9.5(Mar 17, 2020)

    This version is just a bugfix to follow symbolic links when reloading a file (#65) and update versions for the various libraries (like kotlin to 1.3.70)

    Source code(tar.gz)
    Source code(zip)
  • 0.9.41(Apr 30, 2019)

  • 0.9.4(Apr 26, 2019)

    This one is composed mostly by bugfixes and rework on how the tests are done and finally getting rid of Spek 1 which caused a lot of pain when trying to debug things, IDE exceptions etc...

    Bugfixes:

    • Fixed #57
    • Fixed a bug where the equality of ConfigObjects wouldn't be true at some point
    • Now values aren't going to be overridden when a property has a root and children at the same time, a warning will be shown (it was already showing a warning if the root was direct)
    • Now nulls won't be cached in getOrNull inside a CachedConfigProvider
    • Added support for lists inside properties objects. (CATCH: The order of the items in the list will be unpredictable, so don't rely on this if you need a sorted list)

    Rework

    Now instead of Spek 1, Kotlintest is being used (latest version). With this rework I had to think better how to test and increase the test coverage. Also every file has its test file equivalent which was not true before, basically everything was a Functional test!

    Source code(tar.gz)
    Source code(zip)
  • 0.9.3(Mar 5, 2019)

    In this release a huge rework of the source code has been done. Some methods have been removed and some have been added from the API.

    1. Now it supports all kind of complex objects and maps. Everything will be transformed for you (not supported in properties, system properties or environment variables as the process to parse that into a root ConfigObject can be different in each case, you can always implement your own loader to do this). Some examples:
      1. List<List<Int>>
      2. Map<String, Map<Int, List<String>>>
    2. The signature for the Parser has changed: context: ConfigContext, value: ConfigObject, typeStructure: TypeStructure. Now it will receive an object which is the context (similar to Gson for example), where you will be able to parse a subtype or a property from your complex object. A new object has been introduced which is the TypeStructure this will hold the structure of the generic type of your object, for example from List<List<Int>>, the type structure will be this:
      1. type: List<List> and generics: List
      2. type: List and generics: Int
    3. Removed methods from the ConfigProvider interface:
      1. get(name: String, type: Class<T>, default: T?): T
      2. getOrNull(name: String, type: Class<T>, default: T?): T?
    4. Added data class support, now data classes will be instantiated like if they were interfaces but not giving back a proxy but an actual instance of the class.

    Those two methods have been removed in favor of the same method with the change of type, type: Type. These methods should be better used with the extension functions get<List<String>>() as they will hide for you the work of transforming a generic into a Type object.

    Source code(tar.gz)
    Source code(zip)
  • 0.9.0(Aug 18, 2018)

    Now SystemPropertyConfigLoader and EnvironmentConfigLoader extends DefaultConfigLoader so merges and binding will work with them.

    There is one caveat, that is the SystemProperty actually has something like:

    a=1
    a.b=2
    

    Which is perfectly viable in properties. But when translating that to an object it cannot be represented (for example you cannot do that in JSON). If that happens a warning will be emitted and it will only happen with a java property in the SystemPropertyConfigLoader (or if you do the wrong thing in a properties file)

    Source code(tar.gz)
    Source code(zip)
  • 0.8.5(Mar 7, 2018)

    • Added sources for:
      • Bitbucket
      • Github
      • String
      • StringRotation (list of strings that will be rotated on reload)
    • Revamped ByteBuddyBinder and now it holds a cache so classes won't be recompiled everytime
    • Added merge to ConfigLoader so you can merge before adding it to a provider.
    • Reusable TimedReloadStrategy
    • Auth in URLConfigSource
    • Code cleaning up
    Source code(tar.gz)
    Source code(zip)
  • 0.7.0(Aug 5, 2017)

  • 0.5.2(Feb 7, 2017)

    • Now the default binder will be the ProxyBinder
    • Now the OverrideConfigProvider takes a vararg of config providers instead of loaders. This makes the binding and reloadstrategy be separated in each provider so you can have a different streategy for each one. The problem was that you only had one reload strategy and for example a classpath resource file won't change so there is no need to reload them!
    • Now the provider has a new method of "contains" to check if there is instead of checking null later on
    Source code(tar.gz)
    Source code(zip)
  • 0.5(Feb 6, 2017)

    • Added Git config loader
    • Added parsers to deal with time and date
    • Now the settings can be used with a default value, if there is no setting and no default value, an exception will be thrown.
    • Minor code updates
    Source code(tar.gz)
    Source code(zip)
  • 0.4(Jan 28, 2017)

    • Added Support environment variables
    • Added Create SystemSettingConfigLoader
    • Fixed Bytebuddy binding is not working properly
    • Added YAML config loader
    Source code(tar.gz)
    Source code(zip)
  • 0.3(Jan 8, 2017)

    • Added javadocs
    • Added ByteBuddy interface binding (actually creating a subclass at runtime) for a better performance (benchmarks still TODO)
    • Added HOCONConfigLoader
    Source code(tar.gz)
    Source code(zip)
  • 0.2(Dec 25, 2016)

    • OverrideConfigProvider - Now you can have a group of loaders and the first one will be picked and cached
    • License - Added Apache v2.0 license
    • Improved code coverage and tests
    Source code(tar.gz)
    Source code(zip)
Owner
Javier Diaz
Javier Diaz
A sample skeleton backend app built using Spring Boot kotlin, Expedia Kotlin Graphql, Reactive Web that can be deployed to Google App Engine Flexible environmennt

spring-kotlin-gql-gae This is a sample skeleton of a backend app that was built using: Spring Boot(Kotlin) Reactive Web Sprinng Data R2DBC with MYSQL

Dario Mungoi 7 Sep 17, 2022
Droidup - Simple, flexible app self-updater for Android

Droidup - Simple, flexible app self-updater for Android

null 0 May 22, 2022
Amazing and easy to use Accordion Library for Android built with kotlin

AccoLib An easy-to-use, amazing Accordion Library for Android built with kotlin. It reduces the amount of code needed to make Accordions in android, w

Gourav Khunger 6 Jul 4, 2022
Katoot - An easy-to-use (blocking) Kotlin wrapper for Kahoot's REST api

katoot An easy-to-use (blocking) Kotlin wrapper for Kahoot's REST api. Usage Qui

Subham 1 Jul 17, 2022
A custom view for rating which easy to make and use, but function is excellent

QRatingView A custom view for rating which easy to make and use, but function is excellent Effect Picture Properties <declare-styleable name="QRat

QCoder 1 Dec 3, 2021
Bukkit library written in Kotlin to make with compatibility and ease non-playable-character (NPC)

mc-npk Easy to use, fast and efficient library to make non-playable-characters (

Luiz Otávio 3 Aug 4, 2022
🔥The Android Startup library provides a straightforward, performant way to initialize components at the application startup. Both library developers and app developers can use Android Startup to streamline startup sequences and explicitly set the order of initialization.

??The Android Startup library provides a straightforward, performant way to initialize components at the application startup. Both library developers and app developers can use Android Startup to streamline startup sequences and explicitly set the order of initialization.

Rouse 1.3k Dec 30, 2022
Kord-selfbot is discord selfbot library written in kotlin

Kord-selfbot is discord selfbot library written in kotlin

Jombi 1 Aug 16, 2022
Jetpack Compose for Desktop and Web, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.

Jetpack Compose for Desktop and Web, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.

JetBrains 10k Jan 7, 2023
📚 Sample Android Components Architecture on a modular word focused on the scalability, testability and maintainability written in Kotlin, following best practices using Jetpack.

Android Components Architecture in a Modular Word Android Components Architecture in a Modular Word is a sample project that presents modern, 2020 app

Madalin Valceleanu 2.3k Dec 30, 2022
KotlinDL - High-level Deep Learning Framework written in Kotlin and inspired by Keras

Оригинальный репозиторий https://github.com/JetBrains/KotlinDL KotlinDL: High-le

Temur Yunusov 1 Feb 4, 2022
Movie Android App written in Kotlin, MVVM, Clean Architechture, Modularized, Coroutines, Android Architecture Components and Hilt

Movie Android App written in Kotlin, MVVM, Clean Architechture, Modularized, Coroutines, Android Architecture Components and Hilt.

Klejvi Kapaj 16 Dec 27, 2022
Open-Source Forge 1.8.9 Hypixel Duels bot! Planned to support many modes and written in Kotlin.

This project has been moved to a new repository: [HumanDuck23/duck-dueller-v2](https://github.com/HumanDuck23/duck-dueller-v2) Duck Dueller Are you ti

null 2 Aug 29, 2022
AGStateMachineBuilder - a library for easy creation of state machines using advanced concepts of kotlin

This is a library for easy creation of state machines using advanced concepts of kotlin. As of 2/15/2022, this library only works in kotlin, but will be expanded to work in Java later (whenever I get bored enough to rewrite an entire code structure in java :/ )

Sanjay Mohan 1 Feb 19, 2022
Android Multi Theme Switch Library ,use kotlin language ,coroutine ,and so on ...

Magic Mistletoe Android多主题(换肤)切换框架 背景 时隔四年,在网易换肤之前的思路下,做了几点改进,现在完全通过反射创建View,并且在SkinLoadManager中提供一个configCustomAttrs以支持自定义View的属性插队替换 摈弃了之前的AsyncTask

Mistletoe 18 Jun 17, 2022
Tools for Kotlin/Kscript to easy write shell command line in kotlin code

Kscript Tools Easy way to run shell command line in kotlin and other tools Usage Used in kscript: @file:DependsOn("com.sealwu:kscript-tools:1.0.2") Us

Seal 4 Dec 12, 2022
KataContacts written in Kotlin. The main goal is to practice Clean Architecture Development

KataContacts written in Kotlin We are here to practice Clean Architecture Development. Clean Architecture is a way of structuring code. We are going t

Karumi 48 Oct 3, 2022
A simple android Twitter client written in Kotlin

Blum Blum is an unofficial, simple, fast Twitter client written in Kotlin. This project is a complete rewrite of the Java version. Screenshot Build To

Andrea Pivetta 77 Nov 29, 2022
A showcase music app for Android entirely written using Kotlin language

Bandhook Kotlin This project is a small replica of the app I developed some time ago. Bandhook can still be found on Play Store At the moment it will

Antonio Leiva 1.9k Dec 23, 2022