DEV Community

Cover image for Java and Kotlin news: JDK 28 gets structured concurrency and AOT code
Dmitry Protsenko
Dmitry Protsenko

Posted on Originally published at protsenko.dev

Java and Kotlin news: JDK 28 gets structured concurrency and AOT code

A Spring Security upgrade quietly turns DPoP off in your Java resource server, and JDK 28 makes structured concurrency final.

This digest covers 5 to 19 October 2026.

Java

1. Structured concurrency is final (JEP 543)

Structured concurrency runs related subtasks in different threads as one unit. If one fails, the others are cancelled, and the parent can’t return while a child still runs. After seven preview rounds, StructuredTaskScope is final in JDK 28.

try (var scope = StructuredTaskScope.open()) {
    var user  = scope.fork(() -> findUser());
    var order = scope.fork(() -> fetchOrder());
    scope.join(); // throws ExecutionException if a task fails
    return new Response(user.get(), order.get());
}
Enter fullscreen mode Exit fullscreen mode

Code written for JDK 27 only drops the preview flag. Code from JDK 26 has more to change: join() throws ExecutionException instead of FailedException, and TimeoutException is gone. Custom joiners also get a third type parameter for the exception join() throws. JDK 28 comes out in March 2027.

Sources: InfoQ, A N M Bazlur Rahman, JEP 543.

2. The AOT cache stores compiled code (JEP 544)

The JIT compiles hot methods only after warm-up. Project Leyden’s AOT cache already stores loaded classes (JDK 24) and method profiles (JDK 25). JEP 544 adds the compiled native code, so a production run starts with optimized code. The workflow stays the same:

# training run: record the cache
java -XX:AOTCacheOutput=app.aot -jar app.jar
# production run: start from the cache
java -XX:AOTCache=app.aot -jar app.jar
Enter fullscreen mode Exit fullscreen mode

The JEP measured 65 to 80% shorter startup on five framework benchmarks, against 50 to 70% with the cache alone. Serverless functions and services that scale up often gain the most. The compiled code loads only with the training run’s CPU features and GC.

Source: JEP 544.

3. ZGC sizes the heap on its own (JEP 545 and 546)

ZGC is the low-pause garbage collector. Today its heap cap is fixed: the maximum heap size you set, or 25% of RAM by default. JEP 546 adds a mode where ZGC grows and shrinks the heap with the load and gives memory back to other processes. JEP 545 speeds up ZGC’s startup and warm-up. Both are targeted to JDK 28.

java -XX:+UseZGC -XX:+ZAdaptiveHeapSizing -jar app.jar
Enter fullscreen mode Exit fullscreen mode

The mode is opt-in and lifts the default cap to all of RAM minus a small reserve. With both JEPs, JEP 546 calls setting a maximum heap size “neither necessary nor desirable”. Services in containers gain the most.

Sources: JEP 546, Inside Java on JEP 546, Inside Java on JEP 545.

4. A JSON API in the JDK (JEP 540)

Reading one small JSON file has meant adding Jackson or Gson. JEP 540 adds a JSON API to JDK 28 as an incubator module. Alan Bateman and Viktor Klang presented it at Devoxx Belgium. Json.parse returns a tree of JsonValue types:

JsonValue doc = Json.parse("""
    { "name": "Today", "temperature": 54 }
    """);
int temperature = doc.get("temperature").asInt();
Enter fullscreen mode Exit fullscreen mode

There is no data binding and no streaming, so it fits tools and small services that read one config file. Run it with the incubator module added, and expect the API to change:

java --add-modules jdk.incubator.json Weather.java
Enter fullscreen mode Exit fullscreen mode

Sources: JEP 540, Inside Java.

5. Lazy constants: a fourth preview proposed (JEP 547)

A lazy constant computes its value once, on first access, even across threads. The JVM then treats it like a final field and can fold it into the code that reads it.

private final LazyConstant<Logger> logger =
    LazyConstant.of(() ->
        LoggerFactory.getLogger(OrderController.class));

void submitOrder() {
    logger.get().info("order started"); // created once
}
Enter fullscreen mode Exit fullscreen mode

It replaces double-checked locking on a volatile field. JEP 547 is a Candidate for a fourth preview with no changes, after previews in JDK 25 to 27. Keep it out of production code until it is final.

Source: JEP 547.

6. Helidon 27 goes “Tip & Tail”

Helidon is Oracle’s Java framework for microservices. “Tip & Tail” (JEP 14) means two release lines: the tip follows the newest JDK, the tail stays on an older one and gets fixes. Helidon 27 is the tip, on JDK 27. Helidon 4.5.5 is the tail, on JDK 21 with JDK 25 support.

Two modules are new. Helidon Messaging uses plain blocking Java instead of a reactive API, and Helidon Data JDBC generates repository implementations from your interfaces at compile time. Pick the line by your JDK.

Source: InfoQ, Michael Redlich.

7. Smaller releases

  • Open Liberty 26.0.0.10: JDK 27 support, and G1 is now the default GC everywhere.
  • LangChain4j 1.22.0: batch inference on Amazon Bedrock and the OpenAI Decisions API.
  • Gatherers4j 0.15.0: gatherers for moving and running statistics, with breaking changes.
  • JDK 28 early access build 20: try your code on next March’s JDK.

Sources: Open Liberty blog, LangChain4j releases, Gatherers4j releases, JDK 28 early access.

8. Worth reading

Kotlin

9. The Kotlin compiler will need JDK 17

Kotlin 2.5.0 will fail on a JDK older than 17 unless you opt out. From 2.5.20-Beta1 there is no opt-out. This is the JDK that runs the compiler, not your bytecode target. The latest stable release is still 2.4.21.

./gradlew updateDaemonJvm --jvm-version=17
Enter fullscreen mode Exit fullscreen mode

CI images and builds still on a JDK older than 17 break after the upgrade. On Maven, point JAVA_HOME at JDK 17 or newer.

Sources: Kotlin on Bluesky, Kotlin releases.

10. IntelliJ’s Java and Kotlin support in VS Code and Cursor

“Java and Kotlin by IntelliJ IDEA” reached 1.0. It brings IntelliJ’s completion, navigation, refactorings and debugger to VS Code and Cursor through a language server and a debug adapter. In Cursor, the Agents Window can run its own copy of the language server. Install it from the VS Code Marketplace or Open VSX.

Kotlin outside IntelliJ was the weak spot for teams on VS Code and AI editors. From 1.0 the extension needs an IntelliJ IDEA Ultimate subscription, after a 30-day trial.

Source: IntelliJ IDEA blog, Marco Behler.

11. Swift export replaces SKIE in one app

Kotlin Multiplatform shares Kotlin code with iOS. Swift used to see that code through an Objective-C header, and the SKIE plugin patched the gaps, such as sealed classes and Flows. Swift export is Kotlin’s own way to generate Swift directly, and it is still alpha.

John O’Reilly moved his PeopleInSpace app to Swift export on a Kotlin 2.5.0-Beta2 dev build. After trimming what the app exports, the generated Swift fell from about 18k to 2.6k lines. One catch: a function returning a type from a hidden dependency became a fatalError() stub, with no build warning.

Source: John O’Reilly.

12. Google’s agent kit for Kotlin

ADK (Agent Development Kit) is Google’s framework for AI agents. Version 1.4.0 of the Kotlin edition came out on 14 October. It adds a Kotlin DSL for workflow graphs and a backend for OpenAI Chat Completions and compatible APIs. For JVM teams building agents, it is another option next to JetBrains’ Koog and LangChain4j.

Source: GitHub release.

13. Watch and read

Other JVM languages

14. Grails 8 runs on Spring Boot 4.1

Grails is the Groovy web framework built on Spring Boot. Version 8.0 needs Java 21 and Gradle 9.7 or later, and moves to Spring Boot 4.1 and Groovy 5. One line maps every controller as a REST resource:

class UrlMappings {
    static mappings = {
        "/$controller"(resources: '*') // REST mapping for every controller
    }
}
Enter fullscreen mode Exit fullscreen mode

Promises now run on Spring Boot’s task executor, virtual threads included. Upgrading from Grails 7: the Grails 7 Gradle wrapper fails, and the Spring Dependency Management plugin is gone. Separately, Spock 2.5.0 adds Groovy 6 and Spring Framework 7 support.

Sources: Grails blog, James Daugherty, Spock 2.5 release notes.

Security

15. The Spring Security upgrade that switches DPoP off

DPoP (RFC 9449) binds an OAuth access token to a key the client holds. Each request carries a proof signed by that key, so a stolen token is useless without the key. Spring Security 7.0 checked DPoP whenever its classes were on the classpath. Spring Security 7.1, in Spring Boot 4.1, checks it only when you ask. In your security configuration:

@Configuration
class SecurityConfig {

    @Bean
    SecurityFilterChain api(HttpSecurity http) throws Exception {
        http.oauth2ResourceServer(oauth2 -> oauth2
            .jwt(Customizer.withDefaults())
            // Spring Security 7.1: no DPoP check without this line
            .dPoP(Customizer.withDefaults()));
        return http.build();
    }
}
Enter fullscreen mode Exit fullscreen mode

If your DPoP resource server moves from Spring Boot 4.0 to 4.1 without that line, the filter stops checking the proof, and nothing warns you. Two more limits remain: Spring doesn’t implement the DPoP nonce challenge, and the replay cache lives in memory on one node.

Source: InfoQ, Dhruv Agnihotri.

16. Patch list

  • Apache Camel 4.23.0, 4.22.1 and 4.18.5: 38 CVEs, path traversal in the FTP and SMB components among them.
  • Payara Community 7.2026.10: two remote code execution bugs, one of them through the Admin Console.
  • Micronaut 5.2.2: Netty 4.2.19 fixes 16 CVEs.
  • Open Liberty 26.0.0.10: seven CVEs fixed.

Sources: Apache Camel advisories, Payara release notes, Micronaut releases, Open Liberty blog.

Which of these hits your project first?

At the end

If you like this digest, follow me on LinkedIn and Telegram to get the next one. More on Kotlin and Spring: Kotlin data class in Spring Boot 4.

Found this useful? Follow me on social media to stay updated.

Telegram · LinkedIn

Top comments (0)