General
Kora is a cloud-oriented server framework written in Java for applications written in Java and Kotlin.
This page describes the basic principles of Kora, environment requirements, annotation processor setup, minimal Gradle configuration, dependency management, and application startup.
Kora provides a set of modules for quickly building server applications: HTTP server and HTTP client, Kafka consumers, repositories for working with databases, S3 client, gRPC server and gRPC client, Camunda integrations, module telemetry, resilience, and other capabilities.
The main framework characteristics are described on the home page.
Kora provides the tools usually needed for modern server-side development:
- dependency injection through annotations;
- inversion of control without a separate container at runtime;
- aspect-oriented programming through annotations;
- sufficiently high-level simple abstractions and development tools;
- a large set of preconfigured integrations;
- telemetry, tracing, metrics according to the
OpenTelemetrystandard, and module logging; - fast testing with JUnit5;
- working examples and guides.
For high-performance, efficient, and predictable code, Kora follows these principles:
- does not use
Reflectionduring application runtime; - does not use
dynamic proxyduring application runtime; - does not generate bytecode during compilation or application runtime;
- creates source code at compile time through annotation processors;
- keeps thin abstractions over integrations;
- provides free aspects: without additional cost during application runtime;
- uses only the most efficient implementations for integrations;
- encourages and uses the most effective development principles and natural language constructs.
If you need a step-by-step walkthrough before the reference description, see Creating Your First Kora Application and Dependency Injection Introduction.
Annotation Processors¶
Kora builds the application at compile time: processors read annotations, validate the code, and generate source files that are then compiled together with the application code.
As a result, the dependency graph, aspects, HTTP handlers, repositories, and other components become ordinary compiled code without Reflection at runtime.
An annotation is a construct associated with Java source code elements: classes, methods, parameters, and fields.
An annotation processor is started by the compiler, reads these annotations, and can generate additional source code or stop compilation with a clear error.
Kora provides all annotation processors in a single dependency:
This dependency is needed only at compile time and does not add extra libraries to the application runtime classpath.
KSP (Kotlin Symbol Processing) is used for Kotlin.
KSP reads Kotlin source code symbols, passes them to Kora processors, and allows code generation before the main compilation step.
Kora provides KSP processors in a single dependency:
At the same time, Kotlin processing is usually slower than annotation processing in Java.
KSP¶
KSP is needed only for Kotlin projects.
If the application is written in Java, use the regular annotationProcessor; if the application is written in Kotlin, connect com.google.devtools.ksp and the ru.tinkoff.kora:symbol-processors dependency.
Compatibility¶
Requires at least JDK 17, recommended to always use the latest JDK version, or at minimum JDK 25+, to unlock all capabilities of virtual threads.
Minimal configuration in build.gradle:
plugins {
id "java"
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(25)
vendor = JvmVendorSpec.ADOPTIUM
}
}
The vendor pin is optional and simply matches the Adoptium toolchain used by the example projects; you may omit it or select another vendor.
Requires at least JDK 17, recommended JDK 21 because Kotlin versions 1.9+ don't stably support higher JDK versions.
Recommended and supported Kotlin 1.9+, compatibility with versions 1.8+ and 2+ is not guaranteed.
Recommended and supported KSP 1.9+ should match the Kotlin version.
Minimal configuration in build.gradle.kts:
plugins {
kotlin("jvm") version "1.9.25"
id("com.google.devtools.ksp") version "1.9.25-1.0.20"
}
kotlin {
jvmToolchain { languageVersion.set(JavaLanguageVersion.of("21")) }
sourceSets.main { kotlin.srcDir("build/generated/ksp/main/kotlin") }
sourceSets.test { kotlin.srcDir("build/generated/ksp/test/kotlin") }
}
Build System¶
Kora is designed to be built with Gradle because Gradle has good support for annotation processors, KSP, incremental builds, and dependency management.
Requires Gradle 7+, recommended Gradle 9.5+.
To avoid specifying versions for each Kora dependency separately, use the BOM ru.tinkoff.kora:kora-parent.
The BOM version is specified once, and the rest of the Kora dependencies are declared without an explicit version.
Minimal application configuration in build.gradle:
plugins {
id "java"
id "application"
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
vendor = JvmVendorSpec.ADOPTIUM
}
}
dependencies {
annotationProcessor "ru.tinkoff.kora:annotation-processors:1.2.18"
implementation platform("ru.tinkoff.kora:kora-parent:1.2.18")
}
A more detailed example is available in Creating Your First Kora Application.
Gradle Kotlin DSL is assumed for Kotlin.
If the project uses Groovy DSL, follow the Java examples.
Minimal application configuration in build.gradle.kts:
plugins {
id("application")
kotlin("jvm") version "1.9.25"
id("com.google.devtools.ksp") version "1.9.25-1.0.20"
}
kotlin {
jvmToolchain { languageVersion.set(JavaLanguageVersion.of("21")) }
sourceSets.main { kotlin.srcDir("build/generated/ksp/main/kotlin") }
sourceSets.test { kotlin.srcDir("build/generated/ksp/test/kotlin") }
}
dependencies {
ksp("ru.tinkoff.kora:symbol-processors:1.2.18")
implementation(platform("ru.tinkoff.kora:kora-parent:1.2.18"))
}
A more detailed example is available in Creating Your First Kora Application.
In real projects the BOM version is usually extracted into a gradle.properties property (for example koraVersion) and referenced as platform("ru.tinkoff.kora:kora-parent:$koraVersion"), so the version is declared in a single place instead of being hardcoded in every module.
Compiler internal access
Some Java annotation processors read jdk.compiler internals. On newer JDKs this may require exporting the corresponding packages to the compiler.
If compilation fails with IllegalAccessError or module jdk.compiler does not export ... errors, add the following JVM arguments to gradle.properties:
org.gradle.jvmargs=--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \
--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \
--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \
--add-exports jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED \
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
Dependencies¶
Kora module documentation usually shows only the dependency of a specific module.
But the application must also connect the BOM and processors shown below.
build.gradle:
After that, module dependencies can be declared without a version, for example:
Run¶
The application plugin is usually used for local startup and building an executable archive.
[!TIP] It's recommended to always use fixed values
applicationName = "application"andarchiveFileName = "application.tar"— this simplifies working with the archive inDockerfileand CI/CD scripts, as the filename doesn't depend on the project version.
Connect the plugin in build.gradle:
- The
applicationplugin provides tasks for running and building an executable archive (not connected by default, optional)
System properties and environment variables for local startup can be set in the run task:
- JVM arguments for running the application (not set by default, optional)
- Environment variables available in the application (not set by default, optional)
Run:
Archive build configuration:
application {
applicationName = "application" //(1)!
mainClassName = "ru.tinkoff.kora.java.Application" //(2)!
applicationDefaultJvmArgs = ["-Dfile.encoding=UTF-8"] //(3)!
}
distTar {
archiveFileName = "application.tar" //(4)!
}
- Application name, used for naming scripts (default: project name). It's recommended to fix the value
"application"to simplify work inDockerfileand CI/CD. - Fully qualified name of the class with the
mainmethod for running (not set by default, required). - Default JVM arguments for running (not set by default, optional).
- Archive filename (default:
<applicationName>-<version>.tar). It's recommended to fix the value"application.tar"to simplify work inDockerfileand CI/CD. More details in theTartask documentation.
Build the archive:
A configured application example is available in the Java application template.
Connect the plugin in build.gradle.kts:
plugins {
id("application") //(1)!
kotlin("jvm") version "1.9.25"
id("com.google.devtools.ksp") version "1.9.25-1.0.20"
}
- The
applicationplugin provides tasks for running and building an executable archive (not connected by default, optional)
System properties and environment variables for local startup can be set in JavaExec tasks:
tasks.withType<JavaExec> {
jvmArgs(
"-Xmx256m", //(1)!
)
environment(
"SOME_ENV" to "someValue", //(2)!
)
}
- JVM arguments for running the application (not set by default, optional)
- Environment variables available in the application (not set by default, optional)
Run:
Archive build configuration:
application {
applicationName = "application" //(1)!
mainClass.set("ru.tinkoff.kora.kotlin.ApplicationKt") //(2)!
applicationDefaultJvmArgs = listOf("-Dfile.encoding=UTF-8") //(3)!
}
tasks.distTar {
archiveFileName.set("application.tar") //(4)!
}
- Application name, used for naming scripts (default: project name). It's recommended to fix the value
"application"to simplify work inDockerfileand CI/CD. - Fully qualified name of the class with the
mainmethod for running (not set by default, required); for Kotlin this is a class with theKtsuffix. - Default JVM arguments for running (not set by default, optional).
- Archive filename (default:
<applicationName>-<version>.tar). It's recommended to fix the value"application.tar"to simplify work inDockerfileand CI/CD. More details in theTartask documentation.
Build the archive:
A configured application example is available in the Kotlin application template.