KickAss Configuration. An annotation-based configuration system for Java and Kotlin

Overview

KAConf Awesome Kotlin Badge

2016-2020 Mario Macías

KickAss Configuration v0.9.0 is an Annotation-based configuration system inspired in the wonderful Spring Boot.

Its strong points are:

  • Easy to use, integrate and extend
  • Tiny footprint: a single, ~13KB JAR with no third-party dependencies
  • Born from own's necessity, with no over-engineered use cases

Maven Coordinates

<dependency>
    <groupId>info.maciasgroupId>
    <artifactId>kaconfartifactId>
    <version>0.9.0version>
dependency>

Change log

Quick demonstration of usage

  • The @Property annotation allows you to define any field that recevies its value from a configuration source, whatever its visibility is.
public class DbManager {
    @Property("db.username")
    private String user;

    @Property("db.password")
    private String password;

    // ...
}
  • You can even define final and static fields, with default values. Properties that are both final static require to use the KA.def or KA.a[Type] helper methods.
import static info.macias.kaconf.KA.*;

public class Constants {
    @Property("timeout")
    public static final long TIMEOUT_MS = def(1000); // default=1000

    @Property("security.enabled")
    public static final boolean SECURITY_ENABLED = aBoolean();
}
  • The Configurator.configure method will automatically set the values from its configuration sources. You can build a Configurator object with multiple sources and different priorities.
public class SomeController {
    private DbManager dbm;

    public void start() {
        Configurator conf = new ConfiguratorBuilder()
            .addSource(System.getenv()) // most priority
            .addSource(System.getProperties())
            .addSource(JavaUtilPropertySource.from("app.properties"))
            .addSource(JavaUtilPropertySource.from( // least priority
                getClass().getResourceAsStream("defaults.properties")
            )).build();

        conf.configure(Constants.class);
        conf.configure(dbm);
    }
}
  • It's easy to hardcode configuration for testing purposes.
public class TestSuite {

    DbManager dbm = new DbManager();

    public void setUp() {

        Map<String,String> customProperties = new HashMap<>();
        customProperties.put("db.username","admin");
        customProperties.put("db.password","1234");
        customProperties.put("security.enabled", "false");

        Configurator conf = new ConfiguratorBuilder()
            .addSource(customProperties)
            .addSource(new JavaUtilPropertySource(
                getClass().getResourceAsStream("defaults.properties")
            )).build():

        conf.configure(Constants.class);                    
        conf.configure(dbm);
    }
}     

Building and using a Configurator object

The ConfiguratorBuilder class allows building a Configurator object. The ConfiguratorBuilder.addSource method sets the different sources of properties (PropertySource interface). The PropertySource with most priority is the first instance passed as argument to the addSource method, and the PropertySource with least preference is the object passed to the last addSource invocation.

Example of usage:

Configurator conf = new ConfiguratorBuilder()
    .addSource(System.getenv()) // most priority
    .addSource(System.getProperties())
    .addSource(JavaUtilPropertySource.from("app.properties"))
    .addSource(JavaUtilPropertySource.from( // least priority
        getClass().getResourceAsStream("defaults.properties")
    )).build():

The addSource method accepts the next types as argument:

  • java.util.Map
  • java.util.Properties
  • Any implementing class of the PropertySource interface. KAConf bundles two helper implementations:
    • JavaUtilPropertySource
    • MapPropertySource

Once a Configurator object is built, you can pass the configurable object (if object/class properties must be set) or class (if only static fields are willing to be set).

conf.configure(object);
conf.configure(Constants.class);

Default Configurator behaviour

Given the next example properties:

    some.value=1234
    some.other.value=yes
  • Numbers: any property that parses into a number is valid. If not, the Configurator.configure will throw a ConfiguratorException:
@Property("some.value")
private int someValue;       // correct

@Property("some.other.value")
private int someOtherValue;  // throws ConfiguratorException

If the property to look is not on the properties sources, the value will remain as 0, or as the default one.

@Property("value.not.found")
private int value1;           // will be 0

@Property("value.not.found")
private int value2 = def(1000); // will be 1000 (default)

//default valid for non-final & static primitive fields
@Property("value.not.found")
private int value3 = 1000;    // will be 1000 (default)
  • Strings: any property is valid. If the property is not found, the value will be null or the default one.
"1234" @Property("some.other.value") private String someOtherValue; // value -> "yes" @Property("value.not.found") private String value1; // value -> null @Property("value.not.found") private String value2 = def(""); // value -> empty, non-null String //default valid for non-final & static primitive fields @Property("value.not.found") private String value3 = ""; // value -> empty, non-null String">
@Property("some.value")
private String someValue;        // value -> "1234"

@Property("some.other.value")
private String someOtherValue;   // value -> "yes"

@Property("value.not.found")
private String value1;           // value -> null

@Property("value.not.found")
private String value2 = def(""); // value -> empty, non-null String

//default valid for non-final & static primitive fields
@Property("value.not.found")
private String value3 = "";      // value -> empty, non-null String
  • Booleans: any property whose string value exists and is true, 1 or yes will be set as true. Otherwise will be false.
false @Property("some.other.value") private boolean someOtherValue; // value -> true @Property("value.not.found") private boolean value1; // value -> null">
@Property("some.value")
private boolean someValue;       // value -> false

@Property("some.other.value")
private boolean someOtherValue;  // value -> true

@Property("value.not.found")
private boolean value1;          // value -> null
  • Chars: the value of the property will be the first character of a string. Any non-found property will set the value to '\0' or the default one.

  • Boxed primitive types: boxed primitive types will behave as their unboxed equivalents, but properties that are not found will get the null default value.

1234 @Property("not.found.value") private Integer nullableInt; // value --> null">
@Property("some.value")
private Integer intValue;     // value --> 1234

@Property("not.found.value")
private Integer nullableInt;  // value --> null

Mixing naming conventions into a property

When you use multiple configuration sources (e.g. environment variables and Java properties), different naming conventions may apply for the same property.

You can set multiple names for each property, and KAConf will indistinctly use both (in the same priority as the order in the property array).

public class Animal {
    @Property({"ANIMAL_NAME", "animal.name"})
    private final String name;
}

Inherited fields

KAConf allows setting properties that are annotated in the superclass of the configurable object or class. For example:

public class Animal {
    @Property("animal.name")
    private final String name;
}
public class Dog extends Animal {
    @Property("animal.species")
    private final String species;
}

public class PetShop {
    Configurator conf = ...
    public Animal buy() {
        Dog puppy = new Dog();
        conf.configure(puppy);
        return puppy;
    }
}

Adding custom Property Sources

Adding new Property Sources is simple. Usually is enough to extending the AbstractPropertySource class and implementing only two abstract methods:

protected String get(String name);

Which returns the string value of the property named according to the name argument.

boolean isAvailable();

Which returns true if the properties have been successfully read from the source (e.g. a file or DB).

PropertySources failing to load

Any implementation of PropertySource is expected to fail silently (e.g. if it tries to read the values from a file that is not accessible), and then return false in the isAvailable method.

Static final fields

Because of the way the Java compiler inlines the static final fields of primitive types, it is necessary to assign the result of any method call to the declaration of the field. The KA class provides some simple functions to allow that. For example:

@Property("some.property")
public static final int SOME_PROPERTY = KA.def(1234) // default value

@Property("other.property")
protected static final byte OTHER_PROPERTY = KA.aByte(); //defaults to 0

Kotlin basic types support

As my favourite programming language, Kotlin is a first-class citizen in KAConf, and it is fully supported out of the box.

class KonfigurableClass {
    @Property("publicint")
    var publicint = KA.def(321)

    @Property("finalchar")
    val finalchar = KA.def('a')

    companion object {
        @Property("finalstaticint")
        val finalstaticint: Int = 0
    }
}

object KonfigurableObject {
    @Property("aboolean")
    val aboolean = KA.aBoolean()

    @Property("anint")
    var anint: Int? = null
}

Other JVM languages (Scala, Groovy, Ceylon...) have not been tested. ¿Maybe you can test them for me and tell us the results? 😉

Next steps

There are still some potential improvements of interest in KAConf.

To implement in v0.9.x

  • Some refactoring of the Configurator.configure code to be less spaghetti and more efficient
  • Arrays of basic types and strings
  • Analyse Property usages in compile time to warn the user about potential issues (e.g. annotating a final static primitive value without using any helper method from the KA class);
  • Specify mandatory properties (Configurator will throw and exception if the property is not found)
  • Add a description annotation
  • Add a String KAConf.info(...) method that shows string information about the parameters (name, description, etc...)
You might also like...
🔦 Showkase is an annotation-processor based Android library that helps you organize, discover, search and visualize Jetpack Compose UI elements
🔦 Showkase is an annotation-processor based Android library that helps you organize, discover, search and visualize Jetpack Compose UI elements

Showkase is an annotation-processor based Android library that helps you organize, discover, search and visualize Jetpack Compose UI elements. With minimal configuration it generates a UI browser that helps you easily find your components, colors & typography. It also renders your components in common situations like dark mode, right-to-left layouts, and scaled fonts which help in finding issues early.

Easy, asynchronous, annotation-based SOAP for Android

IceSoap IceSoap provides quick, easy, asynchronous access to SOAP web services from Android devices. It allows for SOAP responses to be bound to Java

Annotation based Android lint check generation
Annotation based Android lint check generation

Intervention Annotation based Android lint check generation Generate custom Android lint checks and have lint warn you about code you may be dealing w

Annotation based simple API flavored with AOP to handle new Android runtime permission model

Let Annotation based simple API flavoured with AOP to handle new Android runtime permission model. If you check Google's Samples about the new permiss

KSP-based library that generates lists from your annotation usages

ListGen, Generate Lists From Functions That Have @Listed Annotations! Welcome to ListGen! ListGen is a KSP-based library that can generate lists (and

Annotation-based library for registration commands

Cargo commands Annotation-based library for registration commands Examples: Plugin main class: class ExamplePlugin : Plugin() { override fun regis

A plugin system that runs like a browser, but instead of load web pages, it load apk plugins which runs natively on Android system.
A plugin system that runs like a browser, but instead of load web pages, it load apk plugins which runs natively on Android system.

Android Dynamic Loader Android Dynamic Loader is a plugin system. The host application is like a browser, but instead of load web pages, it load plugi

⌨️ A tool that gives you a massive head start when building Compose based apps. It saves you from time-consuming setup and configuration
⌨️ A tool that gives you a massive head start when building Compose based apps. It saves you from time-consuming setup and configuration

⌨️ A tool that gives you a massive head start when building Compose based apps. It saves you from time-consuming setup and configuration

This is an Kotlin Library that enables Annotation-triggered method call logging for Kotlin Multiplatform.

This is an Kotlin Library that enables Annotation-triggered method call logging for Kotlin Multiplatform.

AndroidQuery is an Android ORM for SQLite and ContentProvider which focuses on easy of use and performances thanks to annotation processing and code generation

WARNING: now that Room is out, I no longer maintain that library. If you need a library to easy access to default android ContentProvider, I would may

An annotation and Kotlin compiler plugin for enforcing a when statement is exhaustive

An annotation and Kotlin compiler plugin for enforcing a when statement is exhaustive

This library provides advance views for lists and stacks. Some of the views are build on top of RecyclerView and others are written in their own. Annotations are compiled by annotation processor to generate bind classes. DOCS -->
This library provides advance views for lists and stacks. Some of the views are build on top of RecyclerView and others are written in their own. Annotations are compiled by annotation processor to generate bind classes. DOCS --

PlaceHolderView An advance view for lists and stacks Some Implementations Documentation You can find the PlaceHolderView documentation here which has

Trail is a simple logging system for Java and Android. Create logs using the same API and the library will detect automatically in which platform the code is running.

Trail Trail is a simple logging system for Java and Android. Create logs using the same API and the library will detect automatically in which platfor

Powerful event-bus optimized for high throughput in multi-threaded applications. Features: Sync and Async event publication, weak/strong references, event filtering, annotation driven
Powerful event-bus optimized for high throughput in multi-threaded applications. Features: Sync and Async event publication, weak/strong references, event filtering, annotation driven

MBassador MBassador is a light-weight, high-performance event bus implementing the publish subscribe pattern. It is designed for ease of use and aims

An annotation processor library that automatically creates Hilt's `@Binds` functions and modules.
An annotation processor library that automatically creates Hilt's `@Binds` functions and modules.

HiltBinder An annotation processor library that automatically creates Hilt's @Binds functions and modules. If you think this library is useful, please

A PyCharm plugin that implements inlay parameter and type annotation hints for Python.
A PyCharm plugin that implements inlay parameter and type annotation hints for Python.

Python Inlay Params Quick Installation Using IDE built-in plugin system: Settings/Preferences Plugins Marketplace Search for "Python Inlay Param

Annotation-triggered method call logging for your debug builds.

Hugo Annotation-triggered method call logging for your debug builds. As a programmer, you often add log statements to print method calls, their argume

Annotation Processor for generating Parcelable code
Annotation Processor for generating Parcelable code

ParcelablePlease An AnnotationProcessor for generating Android Parcelable boilerplate code. See this blog entry for comparison with other parcel libra

Annotation Processing Library. Generates proxy class on top of interface/abstract class, that allows to intercept calls. Also known as a design pattern: proxy, delegate, interceptor.
Annotation Processing Library. Generates proxy class on top of interface/abstract class, that allows to intercept calls. Also known as a design pattern: proxy, delegate, interceptor.

1. AutoProxy Annotation Processing Library. Generates proxy class on top of interface/abstract class, that allows to intercept calls. Also known as a

Comments
  •  Fix JavaUtilPropertySource NPE

    Fix JavaUtilPropertySource NPE

    Hi!

    I was using the JavaUtilPropertySource as source, and got NPEs when passing String, or InputStream as argument. This PR fixes those. I also added a Converter for URI.

    Although I added unit tests to complement them, I can't run the tests (or build the project on my end) because the build config requires (your) OSSRH account. I'm not an expert in gradle, so I couldn't update it to exclude deployments on default build.

    opened by devcsrj 2
  • Java 17 Support

    Java 17 Support

    I've been using kaconf and would really like to continue, but it doesn't seem to work with Java 17.

    Here's the compile error:

    Exception in thread "main" java.lang.RuntimeException: java.lang.NoSuchFieldException: modifiers
            at info.macias.kaconf.Configurator.configure(Configurator.java:83)
            at info.macias.kaconf.Configurator.configure(Configurator.java:56)
            at com.apple.amp.aqe.epilog.App.launch(App.kt:16)
            at com.apple.amp.aqe.epilog.App$Companion.main(App.kt:23)
            at com.apple.amp.aqe.epilog.App.main(App.kt)
    Caused by: java.lang.NoSuchFieldException: modifiers
            at java.base/java.lang.Class.getDeclaredField(Class.java:2610)
            at info.macias.kaconf.Configurator.configure(Configurator.java:79)
            ... 4 more
    
    

    Is there a plan to fix it?

    opened by voytovichs 1
Owner
Mario Macias
Author of book: "Programación en Go (Marcombo ed.)" Software engineer @RedHatOfficial. Before: @newrelic @UPC @midokura @BSCCNS @gameloft and other
Mario Macias
A Java API for generating .java source files.

JavaPoet JavaPoet is a Java API for generating .java source files. Source file generation can be useful when doing things such as annotation processin

Square 10k Jan 2, 2023
A Java Code Generator for Pojo Builders

PojoBuilder - A Code Generator for Pojo Builders Author: Michael Karneim Project Homepage: http://github.com/mkarneim/pojobuilder About The PojoBuilde

Michael Karneim 330 Dec 11, 2022
Pure Java code generation tool for generating a fully functional ContentProvider for Android.

RoboCoP RoboCoP is a Java library that can generate a fully-functional ContentProvider from a simple JSON schema file. Get the latest version from our

Rain 246 Dec 29, 2022
Kotlin code generation for commercetools platform type-safe product-types, reference expansion and custom fields

Kotlin code generation for commercetools platform type-safe product-types, reference expansion and custom fields

null 8 Dec 15, 2022
StoryGen - A simple story generator (or it will be eventually) to learn Kotlin

StoryGen A simple story generator (or it will be eventually) to learn Kotlin. To

Markus Reinke 0 Jan 7, 2022
Mirai-device-generator - Mirai Device Generator with kotlin

Mirai Device Generator Mirai DeviceInfo 生成器 作为插件运行时会提供 BotConfigurationAlterer 服

cssxsh 46 Jan 1, 2023
A custom view styling library for Android that generates the obtainStyledAttributes() and TypedArray boilerplate code for you.

DEPRECATED This project is no longer maintained. Consider using https://github.com/airbnb/paris Barber Barber is your personal custom view stylist. Si

Zac Sweers 716 Dec 30, 2022
Tons of extensively featured packages for Angular, VUE and React Projects

rxweb Clean Code. Built with Purpose Contributing to rxweb framework If you are thinking to make rxweb framework better, that's truly great. You can c

null 376 Jan 4, 2023
Codegeneration tool for isomorphic server and mobile Go apps with gRPC & Protobuf. Share code between your backend, Android & iOS app! :sun_with_face:

Anakin Codegeneration tool for isomorphic server and mobile Go apps with gRPC & Protobuf. Share code between your backend, Android & iOS app! Descript

Kirill Biakov 17 Jun 25, 2020
ConfigurNation - Annotation based Kotlin Interfaces for Android SharedPreferences

ConfigurNation Annotation based Kotlin Interfaces for Android SharedPreferences Installation Gradle Dependencies implementation 'com.izikode.izilib:co

Fanis Veizis 4 Jan 19, 2019