The Rest of the Story: April Edition - JVM Weekly vol. 173
The last few editions were dominated by JDK 27 prep - JEP after JEP after JEP. Which means everything that wasn't a JEP got pushed to this edition.
And the resulting pile is unusually rich in stories: people, governance moves, ecosystem direction, and a few projects that are quietly redrawing the JVM map. Let’s go.
1. April: The Rest of the Story
You might remember from last month’s ROTS that I covered Jonathan Vogel “Java Is Fast. Your Code Might Not Be” - the DevNexus-talk-turned-blog-post about how application-level inefficiencies bottleneck even the fastest runtime. I closed that section with “Thanks Jonathan, can’t wait for the followup articles!” Well - Part 2 dropped in April, and it’s exactly what you’d want from a follow-up. Where Part 1 was the catalogue of anti-patterns, Part 2 is the actual investigation: open the JFR recording in JMC, look at the flame graph, watch one method consume 71% of CPU samples, fix it - and then watch what was previously buried in the noise become visible.
If you read Part 1 and went “yeah but where do I actually start”, Part 2 is that walkthrough. Demo app on GitHub with main (anti-patterns intact) and performance-optimized (fixed) branches - diff them yourself. Part 3 is promised to look at automating the loop with tools that read JFR, rank anti-patterns by hot-path weight, write the code changes, and hand you a diff. I’m... cautiously optimistic about that one. We’ll see.
JetBrains announced on April that Junie CLI - their coding agent introduced back in January 2025 - can now detect any JetBrains IDE running on your workstation and connect to it automatically. On the surface this sounds like a small UX improvement. It is not. This is the most strategically important thing JetBrains has shipped in the agent space this year, and it’s the move that finally makes the “Junie + Air + Central + ACP Registry” story (which we walked through in vol. 168) feel coherent.
Two caveats. First, this is beta and a subscription service - the whole Junie offering is paywalled, this isn’t a free-tier expansion. Second, Android Studio support is “planned”, which means anyone doing serious KMP work today still has to make do with the IntelliJ side of the bridge.
But strategically: if JetBrains keeps this working reliably across their IDE family, “agent that thinks at the same level as my refactor tool” becomes a value proposition a generic CLI cannot match. This is the piece I expect to actually move enterprise adoption needles over the next two quarters.
The end of an era: Project Metropolis goes to a CFV. This one made me a bit melancholic. Vladimir Kozlov initiated a Call For Votes on April 21 to withdraw HotSpot Group sponsorship from the OpenJDK Graal and Metropolis projects - operating under Lazy Consensus rules, voting period ending May 4. Effectively: dissolution.
If you weren’t around for it, Project Metropolis was - and I cannot overstate this - one of the most ambitious experimental tracks in OpenJDK’s history. The premise: rewrite significant parts of HotSpot’s C++ runtime in Java itself. “Java-on-Java.” Replace C2 with a self-hosted JIT compiler written in Java, statically compiled via Substrate VM. The vision was a JVM that bootstrapped itself out of its own legacy C++ codebase, using its own type system to catch its own bugs, with all the maintainability gains that implied. It was the kind of swing-for-the-fences proposal that only a community of people who genuinely believe in their tools makes.
What happened? GraalVM happened. Specifically, GraalVM thrived as an external runtime - and last September’s decision to formally decouple GraalVM from the core Java release cycle made it clear that the strategic future for Graal-as-product was as a separate, vendor-led distribution rather than as the JIT inside mainline OpenJDK. Once that direction was set, the internal Metropolis project - whose entire reason for existing was integrating Graal as the HotSpot JIT - became orphaned by its own success on the outside. The Java-on-Java vision didn’t fail technically. It got out-strategized by ecosystem reality.
For JVM veterans, this is “end of an era” stuff. HotSpot’s C++ foundations are now officially the long-term future. There won’t be a self-hosted mainline JIT, at least not via this initiative. If you ever sat through one of John Rose’s talks about the implications of compiling the JVM in itself - yeah, that future is shelved.
GraalVM itself, of course, continues. It’s still an excellent runtime, still the basis for Native Image, still where most of the actual Graal innovation happens. But the ambition of folding it into the JDK proper is being formally retired. It’s a quiet end to a technically daring experiment, and worth marking the moment.
Apparently February’s “Koog for Java” reveal at JavaOne wasn’t going to be the end of it. JetBrains announced direct integration between Koog - their JVM-native AI agent orchestration runtime - and the Spring AI ecosystem.
The pitch is sensible: Spring AI handles the what (model communication, abstractions, vector stores, beans), Koog handles the how (agent state, history, flow, lifecycle). Koog acts as an “orchestration skin” - you keep using your Spring AI providers and vector stores, but the agent logic lives in Koog’s directed-graph workflow model. The properly interesting piece is what Koog manages on the lifecycle side: native crash recovery preserves execution state across restarts, and automated history compression mitigates LLM context window constraints transparently.
If you’ve built an agentic system in production, you know that “what happens when the JVM bounces mid-conversation” is one of those questions that has no good answer in vanilla LangChain4j or Spring AI alone. You either roll your own checkpointing (good luck) or accept that long-running agents will lose state on restart (also bad). Koog solving this at the framework level - and now plumbing into Spring AI cleanly - is genuinely useful.
Spring patches three CVEs in one go - plus a Spring Cloud Gateway one Quick public-service announcement, because four CVEs in two weeks deserves a flag.
Spring Framework 7.0.7 and 6.2.18 shipped April 17 and patch three CVEs:
CVE-2026-22740 - DoS via multipart temp files in WebFlux. Temp files from multipart requests can fail to be deleted, exhausting disk space.
CVE-2026-22741 - Static resource cache poisoning in Spring MVC and WebFlux. Specially-crafted requests can poison the cache with wrongly-encoded resources, breaking the front-end for everyone else.
CVE-2026-22745 - DoS in static resource handling on Windows specifically. Slow-resolving requests can keep HTTP connections open indefinitely.
A week earlier, Spring Cloud Gateway 4.2.0 hit with CVE-2026-22750 - and this one is a particularly sneaky failure mode. The spring.ssl.bundle configuration property was being silently ignored, defaulting to the default SSL configuration with no log message and no warning. If you set this property believing it was applying a custom bundle, you weren’t getting that bundle and you had no way to know. The “false sense of security” framing in Spring’s writeup is unusually honest and accurate.
None of these are RCE-class, but the cache-poisoning one has clean exploitation paths and the SSL-bundle bypass is exactly the kind of silent misconfiguration that survives in production for ages. If you’re on Spring 7.0.x or 6.2.x: patch. If you’re on Spring Cloud Gateway 4.2.0: patch. If you’re still on Spring 5.x: I’m sorry about your maintenance backlog, and you have bigger problems anyway.
Ivar Grimstad weekly Hashtag Jakarta EE updates remain the most reliable signal on Jakarta EE 12 timing, and the April 19 edition gave us the cleanest milestone schedule we’ve seen so far. The Platform Project has now committed to:
M4 (Apr 1 – May 15, 2026) - Core Profile specs to show progress: REST 5.0-M1, CDI 5.0 milestone/beta (currently at Alpha4), JSON-P 2.2-M1, JSON-B 3.1-M1
M7 (Aug 15 – Sep 30) - Ideally Core Profile released
M9 (Jan – Feb 2027) - Finalize remaining specs
M10 (Feb – Mar 2027) - Platform TCK with ratifying implementation
So the realistic read: Core Profile in Q4 2026, Web Profile and full Platform in Q1–Q2 2027. Same shape as Jakarta EE 11, which is at least predictable.
The active debate worth flagging: whether Jakarta NoSQL gets included in EE 12 at all. Grimstad noted explicit resistance among some members, and called for community voices on either side. If you’ve been using Eclipse JNoSQL in production and want it standardized, this is your window - speak up on the Jakarta EE Platform mailing list before the inclusion decision sticks.
Other specs targeting M2 release: Jakarta Connectors 3.0, Faces 5.0, Transactions 2.1, JSON-P 2.2.
Continuing the JetBrains-makes-everything-an-agent theme: a specialized AI agent skill landed in the kotlin-agent-skills repository targeting Kotlin Multiplatform → Android Gradle Plugin 9.0 migrations. AGP 9.0 demands JDK 21 and Gradle 8.10.2, and the migration involves breaking changes to the build DSL plus the complete removal of the legacy PackagingOptions. Anyone who’s done this manually on a non-trivial KMP project knows the pain.
Couple of observations. First: this is the kind of “agentic skill” that LLMs are actually good at - narrowly-scoped, mechanical, structurally well-defined transformations on a constrained input grammar (Gradle DSL). It’s the agentic-coding sweet spot, and probably more reliably useful than the “vibe-code me a feature” style. Second: the necessity of such tools underscores how deep the Android-Kotlin build stack complexity has gotten. The fact that a major version bump now warrants its own purpose-built migration agent says something - and not entirely a flattering something - about where Gradle DSL ergonomics have ended up.
Either way: if you’re staring down an AGP 9.0 migration on a multi-module KMP codebase, this is worth a look before you do it by hand.
2. Release Radar
TornadoVM 4.0.0 GA
We already mentioned it, but treat tis as a necessary due diligence.
The main feature of TornadoVM 4.0 is genuinely big: a new hardware backend supporting Apple Silicon via the Apple Metal API. After years of Apple Silicon being a second-class citizen for serious GPU compute on the JVM, TornadoVM 4.0 now has a native path. Combined with the OpenCL, PTX, and SPIR-V backends, you can now legitimately develop and prototype heterogeneous Java workloads on a MacBook before deploying to NVIDIA, Intel, or AMD hardware. For anyone working on GPULlama3.java-style projects or AI inference in pure Java, this is the release that makes Mac local development viable.
Helidon 4.4.0
Helidon 4.4.0 (released around JavaOne) is a substantial release: agentic LangChain4j patterns, Helidon JSON (compile-time JSON, incubating), expanded declarative APIs (WebClient, WebSocket, Health Checks, Telemetry, Metrics, CORS, Security), unified OpenTelemetry story including OTLP-exported metrics and configurable OpenTelemetry logging, plus formal entry into Oracle’s Java Verified Portfolio. Notable deprecations: the Jaeger tracing provider is deprecated (OpenTelemetry’s Jaeger exporter is gone), and CORS configuration moved from feature-specific to centralized top-level. Then 4.4.1 followed quickly with bug fixes (gRPC server-side error handling, Prometheus metrics performance, Netty 4.1.130.Final, Apache commons-text 1.15.0). Upgrade path is clean - review the deprecations section, then jump.Release notes.
Hibernate ORM 7.3.0
The headline of the new Hibernate 7.3.0 is improved natural ID support. New KeyType enumeration lets the overloaded find() and findMultiple() methods do natural-ID-based loading, and new @NaturalIdClass annotation mirrors @IdClass semantics for non-aggregated composite natural IDs. If you’ve wrestled with composite business keys (and who hasn’t), this finally aligns the natural-ID path with the JPA-standard identifier path.
LangChain4j 1.13.0
In LangChain4j .1.13.0 new RecoverabilityIT and PendingResponse classes make agentic execution state persistable and recoverable across restarts - solving the same problem Koog tackles from the JetBrains side, and confirming that resilient stateful agents are now a baseline expectation across the JVM AI stack.
Also: a new ClassPathSkillLoader (Anthropic-style “Skills” come to LangChain4j) and a new HibernateContentRetriever running HQL queries as a retrieval source - the Hibernate-as-knowledge-graph pattern Belladelli demoed at JCON Europe (see §1) made flesh in code.
Keycloak 26.6.0
Full RFC 7523 support (JWT profile for OAuth 2.0 client auth/authz grants), experimental support for the OAuth Client ID Metadata Document draft - relevant for serving as an authorization server for the latest MCP spec - and full support for the new Keycloak Test Framework on JUnit 6. The MCP-as-OAuth-resource-server piece is genuinely useful if you’re building agent infrastructure that needs proper auth instead of API-key duct tape. Release notes.
Apache Camel 4.19.0
Possibly the most feature-rich release on this list. New components: Azure Functions, Groovy JSON dataformat, and - interestingly - Spring AI Image, bringing Spring AI’s image generation flow into Camel routes. The PQC component now supports hybrid post-quantum cryptography, key lifecycle management, and platform-wide quantum-safe TLS readiness - which sounds aspirational but aligns with where JEP 527 is going on the JDK side. Plus support for Spring Boot 4.0.
Open Liberty 26.0.0.4 beta
JDK 26 support, new features for Jakarta EE 11 Platform (jakartaee-11.0) and Web Profile (webProfile-11.0), Authentication 3.1, Authorization 3.0, Security 6.0 (Jakarta Security 4.0). The mcpServer-1.0 feature now allows dynamic tool registration, which is the practical thing you need if you want to use MCP for anything beyond a static demo. Combined with last month’s MCP additions, Open Liberty continues being one of the first enterprise Java application servers to take MCP seriously.
CheerpJ 4.3
Leaning Tech shipped CheerpJ 4.3 - the WebAssembly-based JVM that runs unmodified Java applications in the browser. The 4.3 release continues the steady iteration toward “yes, you can actually run this Swing app in your browser tab without installing anything.” For preservation work, edu-tech, and certain very specific enterprise reanimation cases, this remains a remarkable piece of engineering. The r/java thread had the usual mix of amazement and “but why” - both reasonable reactions.
Native Image Config Transformer 1.0.1
Michael Simons released v1.0.1 of his native-image-config-transformer - a Maven Shade Plugin extension that bridges JAR shading and GraalVM’s static analysis requirements. If you’ve ever produced a fat JAR with Shade and then watched GraalVM’s reachability metadata go stale because the plugin renamed your packages but not the metadata files, this fixes that. v1.0.1 adds support for the reachability-metadata-schema-v1.2.0.json format. Niche, high-utility, exactly the kind of “small tool that quietly fixes a real-world pain point” the GraalVM ecosystem benefits from.
Amper 0.10.0
For those keeping track of the JetBrains-tries-to-fix-build-tooling saga: Amper continues iterating on its declarative-YAML-but-not-really build configuration model. Worth watching if you’re tired of Gradle Kotlin DSL complexity, but I’d still call this “interesting experiment, not yet a Gradle replacement for serious projects.”
JBang 0.138.0
JBang now executes WAR files with the same handling as JAR files. Small change, useful if you carry a WAR around for legacy testing reasons. The deprecated isJar() method on Project is preserved for backward compat.
3. GitHub All-Stars
A handful of community projects worth flagging this month - none release-worthy in the formal sense, but interesting enough that I’d hate to lose them in the noise.
Server-Components - Phoenix LiveView, on the JVM
Server-components by vadimv brings the Elixir Phoenix LiveView model to the JVM. Stateful, server-driven rendering over WebSockets, no SPA stack required. The project’s architectural philosophy is the interesting bit: it deliberately rejects “magic” - strict avoidance of annotations and implicit execution flows, in favor of explicit control. Roughly 12,000 lines of modern Java, vertically integrated, near-zero runtime dependencies. The author explicitly cites AI-agent-friendliness as a design constraint - predictable, readable code structures are easier for both humans and LLMs to navigate.
I’m genuinely interested in this one. The “JS-less interactive UI” argument has resurfaced via HTMX, Hotwire, and now LiveView ports - and the JVM has been weirdly underrepresented in this conversation. Worth keeping an eye on.
AlgoFlow - bytecode-instrumentation-based algorithm visualizer
AlgoFlow takes a different route to algorithm visualization: a Java Agent that injects visualization callbacks directly into JVM instructions, capturing state changes (array assignments with computed indices, etc.) without source-level integration. This is genuinely clever - the vast majority of educational visualization tools require either heavy code annotation or a step debugger, and AlgoFlow side-steps both via bytecode instrumentation.
The r/java response was polarized - the Java backend got praise, the AI-generated frontend less so. That’s a shame, because the core technique is the interesting bit. If the developer trades in the AI-generated UI for something more polished, this could be a real teaching tool. Watch this space.
WebAssembly4J 1.0.0 - unified Wasm API for the JVM
WebAssembly4J provides a unified API across multiple Wasm runtimes (Wasmtime, WAMR, Chicory) with dual integration paths: JNI for Java 8/11 compatibility and Project Panama (FFM API) for Java 22+. Continues the trend we covered with swc4j in vol. 168 - the “black box script engine” era is giving way to more transparent, low-latency execution models where Wasm becomes a first-class JVM citizen. If you’re building plugins, sandboxes, or polyglot integration, the Wasm-on-JVM space is suddenly worth following again.
MelodyMatrix 1.0.0 - JavaFX + jdeploy as a delivery model
MelodyMatrix by Frank Delporte isn’t important for what it does (it’s a music-related tool) - it’s important as a case study for how to ship a JavaFX desktop app in 2026. The release walkthrough on Foojay covers the full pipeline: jdeploy for cross-platform native installers, GitHub Actions for the build, auto-update integration, the works.
JavaFX in 2026 is in a much better shape than the discourse credits it for, and projects like this one are why. If you’ve been holding off on a desktop Java tool because “distribution is a nightmare,” read this.
Shroom - “if jvm and c had a baby”
A small Polish-fronted curiosity that I genuinely enjoyed: shroom by Wiktor Kęska a tiny C-like language that compiles to JVM bytecode and uses Panama (Foreign Function & Memory API) for manual memory management. You write code with pointers, malloc/free, and structs; the compiler turns it into .class files; under the hood, every pointer is a MemorySegment, every malloc is Arena.allocate(), and every dereference is segment.get()/segment.set(). The toolchain is the cool bit - ANTLR for parsing, walks an AST, then ASM to emit bytecode, then Panama Linker for native FFI when you want to call into libc.
What sells it for me is that it’s a complete loop: source goes in, real .class files come out, and Kotlin/Java/Scala can call them via plain invokestatic - Sieve.countPrimes(1_000_000) compiles to a regular static method call. The arena modifiers (confined, shared, global, auto) are a particularly nice touch - you control memory lifetime per function, and the arena gets closed (freeing all mallocs in one go) when the function returns. It’s a great teaching artifact for anyone who wanted to understand how Panama, ANTLR, and ASM actually fit together.
And from the license section: “Experimental. No license. Don’t use in production. If it breaks, you get to keep both pieces.” Exactly the right energy.
Requires JDK 22+ (since Panama is finalized there). Star it for the educational value alone - this is the kind of repo I’ll be sending people who want to actually grok how the pieces of the modern JVM puzzle slot together.
Controller-Client - Spring Boot tests without the MockMvc boilerplate
From the same author as shroom: controller-client is a much more practical offering - a Spring Boot testing library that wraps MockMvc with dynamic proxies so you can call your controller methods directly in tests, type-safe, no JSON-string-massaging required. Instead of the usual ceremony:
String responseContent = mockMvc.perform(get("/example"))
.andExpect(status().isOk())
.andReturn()
.getResponse()
.getContentAsString();
ExampleResponse response = objectMapper.readValue(responseContent, ExampleResponse.class);you get
@AutowireControllerClient
private ExampleController exampleController;
@Test
void basicGet() {
var response = exampleController.exampleMethod();
assertThat(response.message()).isEqualTo("Hello world!");
}That’s the whole pitch, and honestly it’s enough - anyone who’s written ten MockMvc tests has felt the boilerplate fatigue. The library is at v2.3.0 (December 2025), Spring Web only, and per the README “still in early stage, so it doesn’t support all spring web annotations” - but the core pattern is right and the customizer hook (ControllerClientAnnotationCustomizer, e.g. for adding CSRF tokens to every request) is extensible enough to cover the common needs.
Available on Maven Central as io.github.1grzyb1:controller-client:2.3.0. Worth a look if you’re tired of writing objectMapper.readValue(...) for the hundredth time.
PS: KotlinConf 2026 is around the corner - reminder that I’ll be there as media partner. Come say hi.
PS2: Heads-up that next month is going to be heavy on retro, because both the Vibe Engineering MEAP is hitting milestones and there’s a NYC Tech Week event in early June tied to the book. Expect some self-promotion. I’ll keep it short.











