Bootique Kotlin Module - v3.0


1. Overview

bootique-kotlin contains following modules:

  1. Kotlin APIs and extensions for Bootique;

  2. Kotlin Script Configuration Module;

  3. Configuration and Extensions for Bootique Modules;

  4. JacksonService which provides ObjectMapper with enabled KotlinModule.

2. TL;DR;

  • Use KotlinBootique instead of Bootique;

  • Use KotlinModule instead of Module, you can use KotlinModule with ConfigModule (just inherit both);

  • Use KotlinBQModuleProvider instead of BQModuleProvider;

  • Use extensions defined in Extensions.kt;

  • Use bootique-kotlin-configuration module to benefit from configuration written in Kotlin.

  • Use bootique-kotlin-jackson to get ObjectMapper with KotlinModule.

3. Getting started

Kotlin 1.4.10 used in project.

Latest stable version: Maven Central

Add dependency on needed parts in your build.gradle, or pom.xml:

// Kotlin Extensions for Bootique
compile("io.bootique.kotlin:bootique-kotlin:3.0.M2")

// Kotlin Configuration Module
compile("io.bootique.kotlin:bootique-kotlin-config:3.0.M2")

// Kotlin Configuration Module
compile("io.bootique.kotlin:bootique-kotlin-jackson:3.0.M2")

// Kotlin Configuration and Extensions for Jetty. Also this adds dependency to bootique-jetty module.
compile("io.bootique.kotlin:bootique-kotlin-jetty:3.0.M2")

// Kotlin Configuration and Extensions for Logback. Also this adds dependency to bootique-logback module.
compile("io.bootique.kotlin:bootique-kotlin-logback:3.0.M2")

// Kotlin Configuration and Extensions for $moduleName$. Also this adds dependency to bootique-$moduleName$ module.
compile("io.bootique.kotlin:bootique-kotlin-$moduleName$:3.0.M2")

bootique-kotlin modules doesn’t include kotlin-stdlib-jdk8, or any other core kotlin libraries, since you can use newer kotlin version and usually you already have kotlin-stdlib-jdk8 in runtime. So there are list of additional dependencies for different bootique-kotlin modules:

bootique-kotlin-config:
  org.jetbrains.kotlin:kotlin-scripting-jvm-host

bootique-kotlin-jackson:
  org.jetbrains.kotlin:kotlin-reflect

If you use different kotlin version, it’s much simpler to include these libraries with proper version, instead of excluding library version of them and then including again.

4. Bootique

4.1. KotlinBootique

bootique-kotlin provides replacement for Bootique class - KotlinBootique:

fun main(args: Array<String>) {
    KotlinBootique(args)
        .module(ApplicationModule::class)
        .exec()
        .exit()
}

So no need for extensions for Bootique class, KotlinBootique provides best experience for developing Bootique apps with Kotlin.

4.2. KotlinBQModuleProvider

KotlinBQModuleProvider - interface to implement in Bootique Kotlin application instead of BQModuleProvider.

class ApplicationModuleProvider : KotlinBQModuleProvider {
    override val module = ApplicationModule()
    override val overrides = listOf(BQCoreModule::class)
    override val dependencies = listOf(KotlinConfigModule::class)
}

You can see how declarative become module provider.

4.3. ConfigurationFactory

// Using Java Api
configurationFactory.config(SampleFactory::class.java, "sample")

// With Extension
configurationFactory.config(SampleFactory::class, "sample")

// With Extension, reified generics
configurationFactory.config<SampleFactory>("sample")

// Type Inference
@Singleton
@Provides
fun createAppConfiguration(configurationFactory: ConfigurationFactory): SampleFactory {
    return configurationFactory.config/* No Type Here */(configPrefix)
}

4.4. Adding command

Straightforward and easy to use extension for contributing commands.

BQCoreModule
    .extend(binder)
    .addCommand(ApplicationCommand::class)

4.5. Default command

Also extension for setDefaultCommand available.

BQCoreModule
    .extend(binder)
    .setDefaultCommand(ApplicationCommand::class)

4.6. Bootique DI

4.6.1. KotlinModule

bootique-kotlin introduces new module interface to use with kotlin: KotlinModule

class ApplicationModule : KotlinModule {
    override fun configure(binder: KotlinBinder) {
        binder.bind(ShareCountService::class).to(DefaultShareCountService::class).asSingleton()
        binder.bind(HttpClient::class).to(DefaultHttpClient::class).asSingleton()
    }
}

4.6.2. Extensions

There are few functions to help work with TypeLiteral and Key.

// TypeLiteral
typeLiteral<Array<String>>()

// Key
key<List<Callable<A>>>()

5. Configuration Module

Using Kotlin Script for a configuration is really simple:

  1. Create script

  2. Override ConfigurationFactory

5.1. Using Kotlin Script file

Configuration with Kotlin can be defined in Kotlin Script file:

import io.bootique.kotlin.config.modules.httpConnector
import io.bootique.kotlin.config.modules.jetty

jetty {
    httpConnector {
        port = 4242
        host = "0.0.0.0"
    }
}

5.2. Enable Kotlin Script Configuration

Enable Kotlin Script Configuration in Bootique with extension:

fun main(args: Array<String>) {
    KotlinBootique(args)
        .withKotlinConfig() // Extension function
        .autoLoadModules()
        .exec()
        .exit()
}

Using BQModuleProvider:

fun main(args: Array<String>) {
    KotlinBootique(args)
        .module(KotlinConfigModuleProvider())
        .autoLoadModules()
        .exec()
        .exit()
}

You can pass this file as always to bootique:

./bin/application --config=classpath:config.bq.kts --server

It’s even support multiple files (each file contains map of configs):

./bin/application --config=classpath:config.bq.kts --config=classpath:config1.bq.kts --server

That’s it! You get autocomplete in IDE, and code for a configuration!

6. Bootique Jetty

Define empty config:

jetty {

}

Use autocompletion to define configuration.

Use httpConnector/httpsConnector extensions to define connectors:

jetty {
    httpConnector {
        port = 4242
        host = "192.168.0.1"
        responseHeaderSize = 42
        requestHeaderSize = 13
    }
}

7. Bootique Logback

Define logback configuration:

addConfig("log", logbackContextFactory(
    logFormat = "[%d{dd/MMM/yyyy:HH:mm:ss}] %t %-5p %c{1}: %m%n",
    useLogbackConfig = false,
    debugLogback = false,
    level = LogbackLevel.warn,
    loggers = mapOf(
        logger(LogbackModuleTest::class, LogbackLevel.error),
        logger("TestLogger", LogbackLevel.trace)
    ),
    appenders = listOf(
        consoleAppender(
            logFormat = "[%d{dd/MMM/yyyy:HH:mm:ss}] %t %-5p %c{1}: %m%n",
            target = ConsoleTarget.stderr
        ),
        fileAppender(logFormat, "abc", timeBasedPolicy(
            fileNamePattern = "Abc_%d",
            totalSize = "2m",
            historySize = 1
        ))
    )
))

Use function for retrieving logger for class:

val logger = logger<SomeService>()

Or if class is generic:

val logger = logger<SomeService<*>>()

8. Bootique Undertow

Define undertow configuration:

addConfig("undertow", undertowFactory(
    httpListeners = listOf(
        httpListener(1337, "127.0.0.1")
    ),
    workerThreads = 42
))