Javalin RFCs CI

Various experimental extensions to Javalin 4.x used in Reposilite 3.x. Provides basic support for Kotlin coroutines and async routes with a set of useful utilities.

repositories {
    maven { url 'https://repo.panda-lang.org/releases' }
}

dependencies {
    val version = "1.0.8"
    implementation "com.reposilite.javalin-rfcs:javalin-context:$version"
    implementation "com.reposilite.javalin-rfcs:javalin-reactive-routing:$version"
}

Project also includes panda-lang :: expressible library as a dependency. It’s mainly used to provide Result<VALUE, ERROR> type and associated utilities.

Reactive Routing

Experimental router plugin that supports generic route registration with custom context and multiple routes within the same endpoints.

<div class="highlight highlight-source-kotlin position-relative" data-snippet-clipboard-copy-content="// Custom context
class AppContext(val context: Context)

// Some dependencies
class ExampleFacade

// Endpoint (domain router)
class ExampleEndpoint(private val exampleFacade: ExampleFacade) : AbstractRoutes() {

private val sync = route("/sync", GET, async = false) { blockingDelay("Sync") }

private val blockingAsync = route("/async-blocking", GET) { blockingDelay("Blocking Async") }

private val nonBlockingAsync = route("/async", GET) { nonBlockingDelay("Non-blocking Async") }

override val routes = setOf(sync, blockingAsync, nonBlockingAsync)

}

private suspend fun nonBlockingDelay(message: String): String = delay(100L).let { message }
@Suppress("BlockingMethodInNonBlockingContext")
private suspend fun blockingDelay(message: String): String = sleep(100L).let { message }

fun main() {
val exampleFacade = ExampleFacade()
val exampleEndpoint = ExampleEndpoint(exampleFacade)

val sharedThreadPool = QueuedThreadPool(4)
val dispatcher = DispatcherWithShutdown(sharedThreadPool.asCoroutineDispatcher())
sharedThreadPool.start()

Javalin
.create { config ->
config.server { Server(sharedThreadPool) }

ReactiveRoutingPlugin(
errorConsumer = { name, throwable -> println("$name: ${throwable.message}") },
dispatcher = dispatcher,
syncHandler = { ctx, route -> route.handler(AppContext(ctx)) },
asyncHandler = { ctx, route, _ -> route.handler(AppContext(ctx)) }
)
.registerRoutes(exampleEndpoint)
.let { config.registerPlugin(it) }
}
.events {
it.serverStopping { dispatcher.prepareShutdown() }
it.serverStopped { dispatcher.completeShutdown() }
}
.start("127.0.0.1", 8080)
}
“>

// Custom context
class AppContext(val context: Context)

// Some dependencies
class ExampleFacade

// Endpoint (domain router)
class ExampleEndpoint(private val exampleFacade: ExampleFacade) : AbstractRoutes<AppContext, Unit>() {

    private val sync = route("/sync", GET, async = false) { blockingDelay("Sync") }

    private val blockingAsync = route("/async-blocking", GET) { blockingDelay("Blocking Async") }

    private val nonBlockingAsync = route("/async", GET) { nonBlockingDelay("Non-blocking Async") }

    override val routes = setOf(sync, blockingAsync, nonBlockingAsync)

}

private suspend fun nonBlockingDelay(message: String): String = delay(100L).let { message }
@Suppress("BlockingMethodInNonBlockingContext")
private suspend fun blockingDelay(message: String): String =  sleep(100L).let { message }

fun main() {
    val exampleFacade = ExampleFacade()
    val exampleEndpoint = ExampleEndpoint(exampleFacade)

    val sharedThreadPool = QueuedThreadPool(4)
    val dispatcher = DispatcherWithShutdown(sharedThreadPool.asCoroutineDispatcher())
    sharedThreadPool.start()

    Javalin
        .create { config ->
            config.server { Server(sharedThreadPool) }

            ReactiveRoutingPlugin<AppContext, Unit>(
                errorConsumer = { name, throwable -> println("$name: ${throwable.message}") },
                dispatcher = dispatcher,
                syncHandler = { ctx, route -> route.handler(AppContext(ctx)) },
                asyncHandler = { ctx, route, _ -> route.handler(AppContext(ctx)) }
            )
            .registerRoutes(exampleEndpoint)
            .let { config.registerPlugin(it) }
        }
        .events {
            it.serverStopping { dispatcher.prepareShutdown() }
            it.serverStopped { dispatcher.completeShutdown() }
        }
        .start("127.0.0.1", 8080)
}