# Kora Project Setup Java

> Scaffold a new Java Kora service (Gradle) — @KoraApp root, kora-parent BOM, annotation-processors, koraBom, wrapper. Use when starting a Java project or fixing "annotation processor did not run"/"ApplicationGraph not found". For Kotlin see kora-project-setup-kotlin.

- Skill: `kora-projects/kora-project-setup-java` (Agent Skill, multi-file: 9 files)
- Install (CLI): `npx skillmds@latest add kora-projects/kora-project-setup-java`
- Raw SKILL.md: https://api.skillmd.com/api/skills/kora-projects/kora-project-setup-java/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: kora-projects (https://skillmd.com/u/kora-projects)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/kora-projects/kora-project-setup-java

---


# Kora Project Setup — Java

> **Kora sub-skill — obey the [kora-v1 meta rules](../../SKILL.md) on every task:** **R0** ensure `.kora-agent/` docs+examples are cloned · **R1** read this sub-skill before writing code · **R2** Kora APIs only — no Spring/Micronaut/Quarkus, no invented annotations or config keys · **R3** journal any incorrect Kora usage. Add comments/Javadoc only if asked.

Scaffold a minimal, compilable Kora service in Java. Kora is a compile-time
framework: its annotation processor generates `ApplicationGraph`, controllers,
JSON readers/writers and aspects during `compileJava`. If the processor is not
wired into the Gradle build, **nothing is generated and nothing works**. This
skill gets that wiring right the first time.

**BOM version:** `ru.tinkoff.kora:kora-parent:1.2.19` (declared once; every
`ru.tinkoff.kora:*` artifact inherits it — never version them individually).
**JDK:** 17 minimum, 25 recommended. **Gradle:** 9+ (wrapper pins 9.5.1).

---

## Quick Start

Smallest build that compiles and runs an HTTP endpoint. Mirrors
`.kora-agent/kora-examples/guides/java/kora-java-guide-getting-started-app`.

`build.gradle`:

```groovy
import org.gradle.jvm.toolchain.JavaLanguageVersion
import org.gradle.jvm.toolchain.JvmVendorSpec

plugins {
    id "java"
    id "application"
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(25)
        vendor = JvmVendorSpec.ADOPTIUM
    }
}

repositories {
    mavenCentral()
}

configurations {
    koraBom
    annotationProcessor.extendsFrom(koraBom)
    compileOnly.extendsFrom(koraBom)
    implementation.extendsFrom(koraBom)
    testImplementation.extendsFrom(koraBom)
    testAnnotationProcessor.extendsFrom(koraBom)
}

dependencies {
    koraBom platform("ru.tinkoff.kora:kora-parent:1.2.19")

    // Mandatory: without this nothing is generated.
    annotationProcessor "ru.tinkoff.kora:annotation-processors"

    implementation "ru.tinkoff.kora:http-server-undertow"
    implementation "ru.tinkoff.kora:config-hocon"
    implementation "ru.tinkoff.kora:json-module"
    implementation "ru.tinkoff.kora:logging-logback"

    testAnnotationProcessor "ru.tinkoff.kora:annotation-processors"
    testImplementation "ru.tinkoff.kora:test-junit5"
}

application {
    mainClass = "com.example.Application"
}
```

`settings.gradle`:

```groovy
plugins {
    id "org.gradle.toolchains.foojay-resolver-convention" version "1.0.0"
}

rootProject.name = "kora-example"
```

`src/main/java/com/example/Application.java`:

```java
package com.example;

import ru.tinkoff.kora.application.graph.KoraApplication;
import ru.tinkoff.kora.common.KoraApp;
import ru.tinkoff.kora.config.hocon.HoconConfigModule;
import ru.tinkoff.kora.http.server.undertow.UndertowHttpServerModule;
import ru.tinkoff.kora.json.module.JsonModule;
import ru.tinkoff.kora.logging.logback.LogbackModule;

@KoraApp
public interface Application extends
        HoconConfigModule,
        JsonModule,
        LogbackModule,
        UndertowHttpServerModule {

    static void main(String[] args) {
        KoraApplication.run(ApplicationGraph::graph);
    }
}
```

`ApplicationGraph` is generated by the annotation processor from the `@KoraApp`
interface; it appears in `build/generated/sources/annotationProcessor/` after
the first compile. `ApplicationGraph::graph` is the entry point passed to
`KoraApplication.run`.

`src/main/java/com/example/HelloController.java`:

```java
package com.example;

import ru.tinkoff.kora.common.Component;
import ru.tinkoff.kora.http.common.HttpMethod;
import ru.tinkoff.kora.http.common.annotation.HttpRoute;
import ru.tinkoff.kora.http.common.body.HttpBody;
import ru.tinkoff.kora.http.server.common.HttpServerResponse;
import ru.tinkoff.kora.http.server.common.annotation.HttpController;

@Component
@HttpController
public final class HelloController {

    @HttpRoute(method = HttpMethod.GET, path = "/hello")
    public HttpServerResponse hello() {
        return HttpServerResponse.of(200, HttpBody.plaintext("Hello, Kora!"));
    }
}
```

`src/main/resources/application.conf`:

```hocon
httpServer {
  publicApiHttpPort = 8080
  privateApiHttpPort = 8085
}

logging.level {
  "root": "WARN"
  "ru.tinkoff.kora": "INFO"
}
```

Build and run:

```bash
./gradlew clean build   # runs the annotation processor, builds the graph
./gradlew run           # GET http://localhost:8080/hello -> "Hello, Kora!"
```

---

## Project structure

```
my-app/
├── build.gradle
├── settings.gradle
├── gradle.properties
├── gradle/wrapper/gradle-wrapper.properties
├── src/main/java/com/example/Application.java
├── src/main/resources/application.conf
├── src/main/resources/logback.xml
└── src/test/java/com/example/
```

---

## What's in `references/` and `assets/`

| File | Purpose |
|------|---------|
| [`references/build-gradle-reference.md`](references/build-gradle-reference.md) | Fully annotated `build.gradle`, the `koraBom` configuration explained, test/run tuning, distribution packaging |
| [`references/troubleshooting-reference.md`](references/troubleshooting-reference.md) | Build-error symptom → cause → fix table (processor not run, `ApplicationGraph` missing, dependency not found, daemon hangs) |
| [`assets/build.gradle.template`](assets/build.gradle.template) | Drop-in `build.gradle` |
| [`assets/settings.gradle.template`](assets/settings.gradle.template) | `settings.gradle` with the foojay toolchain resolver |
| [`assets/gradle.properties`](assets/gradle.properties) | JVM args and Gradle flags |
| [`assets/Application.java.template`](assets/Application.java.template) | `@KoraApp` graph root |
| [`assets/gradle-wrapper.properties`](assets/gradle-wrapper.properties) | Gradle wrapper distribution |

---

## When to use vs NOT

**Use this skill when:**
- Creating a Java Kora service from scratch (build files + `@KoraApp` root).
- A build fails with "annotation processor did not run", a missing
  `ApplicationGraph`, or "Required dependency was not found".
- Choosing the JDK toolchain, the `kora-parent` BOM version, or the Gradle
  wrapper for a Kora project.

**Do NOT use this skill for:**
- Kotlin projects → `kora-project-setup-kotlin` (uses `ksp` +
  `symbol-processors`, not `annotationProcessor`).
- Adding HTTP / Database / Kafka / gRPC modules to an existing build →
  `kora-project-dependencies`.
- Writing `@ConfigSource` typed config → `kora-config-hocon`.
- DI patterns (`@Component`, `@Module`, factories) → `kora-di-compile`.

---

## The four things that must be right

1. **`koraBom` configuration wiring.** A custom `koraBom` configuration holds
   the `platform("ru.tinkoff.kora:kora-parent:1.2.19")` and is extended by
   `annotationProcessor`, `implementation`, `compileOnly`, and the `test*`
   configurations. The annotation-processor classpath is separate from the
   application classpath, so it needs the BOM explicitly — otherwise the
   processor resolves without a version and fails.

2. **`annotationProcessor "ru.tinkoff.kora:annotation-processors"`.** This is
   the single processor that generates the graph, controllers, JSON
   readers/writers, and aspects. Add `testAnnotationProcessor` too so
   `@KoraAppTest` works.

3. **`@KoraApp` graph root.** An `interface` annotated with `@KoraApp` that
   `extends` the framework `*Module` interfaces it needs. `main` calls
   `KoraApplication.run(ApplicationGraph::graph)` — the generated
   `ApplicationGraph` lives in the same package.

4. **Correct imports.** `@KoraApp` is `ru.tinkoff.kora.common.KoraApp`;
   `KoraApplication` is `ru.tinkoff.kora.application.graph.KoraApplication`;
   `@Component` is `ru.tinkoff.kora.common.Component`.

---

## Core patterns

### Modules are interfaces the `@KoraApp` extends

Framework capabilities ship as `*Module` interfaces. The graph root pulls them
in via `extends`; each module contributes component factories to the graph.

```java
@KoraApp
public interface Application extends
        HoconConfigModule,        // config-hocon
        JsonModule,               // json-module
        LogbackModule,            // logging-logback
        UndertowHttpServerModule  // http-server-undertow
{ ... }
```

Each `extends` must be backed by an `implementation "ru.tinkoff.kora:<artifact>"`
in `build.gradle`. If a module is on the build path but not extended, its
factories are not added to the graph.

### Your code joins the graph via `@Component`

A class annotated with `@Component` becomes a managed node. Dependencies are
declared as **constructor parameters** — Kora resolves them at compile time.

```java
@Component
@HttpController
public final class HelloController {
    private final GreetingService service;        // resolved from the graph

    public HelloController(GreetingService service) {
        this.service = service;
    }
}
```

Do not use field injection. Kora wires components only through constructors.

### A minimal `@KoraAppTest`

`test-junit5` provides `@KoraAppTest`, which builds the real graph and injects
components into the test via `@TestComponent`.

```java
import static org.junit.jupiter.api.Assertions.assertNotNull;

import org.junit.jupiter.api.Test;
import ru.tinkoff.kora.test.extension.junit5.KoraAppTest;
import ru.tinkoff.kora.test.extension.junit5.TestComponent;

@KoraAppTest(Application.class)
class ApplicationTest {

    @TestComponent
    private HelloController controller;

    @Test
    void controllerIsWired() {
        assertNotNull(controller);
    }
}
```

Requires `testAnnotationProcessor "ru.tinkoff.kora:annotation-processors"` so
the test graph is generated. Deeper testing → `kora-testing-junit-java`.

---

## Common pitfalls

| Symptom | Cause | Fix |
|---------|-------|-----|
| No generated classes; `ApplicationGraph` unresolved | `annotationProcessor "ru.tinkoff.kora:annotation-processors"` missing | Add it to `dependencies` |
| "Could not resolve ru.tinkoff.kora:annotation-processors" (no version) | `annotationProcessor` does not extend `koraBom` | `annotationProcessor.extendsFrom(koraBom)` |
| Module factories absent from the graph | Module on classpath but not in `@KoraApp extends` | Add the `*Module` to `extends` |
| `cannot find symbol KoraApp` / `KoraApplication` | Wrong import package | Use `ru.tinkoff.kora.common.KoraApp` and `ru.tinkoff.kora.application.graph.KoraApplication` |
| `@KoraAppTest` finds no components | `testAnnotationProcessor` missing | Add `testAnnotationProcessor "ru.tinkoff.kora:annotation-processors"` |
| Build hangs after `clean` | Stale Gradle daemon | `./gradlew --stop`, retry |
| IDE shows red but `./gradlew classes` passes | IDE has not indexed `build/generated/` | Re-run `classes`, refresh/invalidate IDE caches |

Full diagnosis table: [`references/troubleshooting-reference.md`](references/troubleshooting-reference.md).

---

## Next steps

- [`kora-project-dependencies`](../kora-project-dependencies/SKILL.md) — add HTTP, Database, Kafka, gRPC, S3 modules.
- [`kora-config-hocon`](../kora-config-hocon/SKILL.md) — typed `@ConfigSource` configuration.
- [`kora-di-compile`](../kora-di-compile/SKILL.md) — compile-time DI patterns.
- [`kora-testing-junit-java`](../kora-testing-junit-java/SKILL.md) — `@KoraAppTest` testing.

