The Rest of the Story: October Edition - JVM Weekly vol. 197

Two threads this month.

The first is security, and it arrived in volume: the eMudhra keys fell to anyone who tried the first 256 numbers, OpenJDK shipped two emergency rounds within four days on top of the regular October update, and Spring moved all its releases to a single “Patch Thursday” because security reports now arrive at nearly 80 a month.

Thanks for reading JVM Weekly! Subscribe for free to receive new posts and support my work.

The second is tools that now have two audiences. JetBrains took IntelliJ’s Java engine to VS Code and Cursor and promised a separate license for agents, a version Netflix had already built itself. Spock shipped its documentation in Markdown “for AI agents”, and on jdk-dev Alan Bateman suggested making 𝚓𝚍𝚋 easier for agents to drive.

Plus TornadoVM finished the JVMCI story from September by writing its own compiler.

1. October: The Rest of the Story

Definition of epic fail? A certificate authority generating its private keys from the numbers 1 to 255.

On October 17 a person identified only as Wayne emailed Apple, Chrome, Microsoft, Mozilla and eMudhra, an Indian CA, to say he had recovered private keys of eMudhra intermediate certificates. Dennis Jackson of Mozilla explained in the Mozilla bug how hard that was: “The 8 impacted keys all used integers less than 256. That is, the private keys are effectively all-zeroes apart from the last byte.” A P-384 key should be a random number of about 384 bits. These fit in one byte, so you can find them by generating the first 256 keys and comparing.

The broken keys belong to eight intermediate certificates, the ones a CA actually signs websites with: six for TLS and two for code signing. Sectigo cross-signed each one, so 16 certificates carry the bad keys. With those keys anyone could issue a certificate browsers would accept for any domain. eMudhra revoked them on October 18. Browsers block bad certificates through lists that update on their own, such as Mozilla’s OneCRL: Firefox and Chrome from October 17, Microsoft from the 20th, Apple from the 22nd.

The JVM has no such mechanism. It trusts what is in 𝚌𝚊𝚌𝚎𝚛𝚝𝚜, the keystore that ships with the runtime, and revocation checking is off by default. So the fix is a new JDK build. On October 21 Andrew H. Gross published the OpenJDK advisory for CVE-2026-91403, with CVSS 9.1 and builds 8u512, 11.0.33.0.2, 17.0.21.0.2, 21.0.13.0.2, 25.0.5.0.2 and 27.0.1.0.2. On October 24 a second advisory followed, for CVE-2026-114820. It lists the .𝟶.𝟸 builds from three days earlier as affected. The fixed builds now end in .𝟶.𝟹, plus 11.0.33 and 8u512. The advisory does not explain what the second fix adds. Mark Reinhold posted both rounds to the announce list, the second as a link to openjdk.org/emudhra.

That page is the one to read. It asks you to “take one of the following actions immediately”, “even if you have updated your runtime in the past few days”. The best option is a runtime whose release notes mention JDK-8395690. Until your vendor has one, the page gives the 𝚔𝚎𝚢𝚝𝚘𝚘𝚕 commands that delete four roots from 𝚌𝚊𝚌𝚎𝚛𝚝𝚜.

One of those roots is Sectigo’s AAA Certificate Services, which signed the cross-certificates. Guillaume Laforge explains why the keytool commands circulating online “give a false sense of security”: “If you only delete emsigneccrootcag3 from cacerts, your JVM is still 100% vulnerable to the 6 \*-aaa-emudhra TLS chains”. His fix is different. He appends SHA-256 fingerprints to $𝙹𝙰𝚅𝙰_𝙷𝙾𝙼𝙴/𝚕𝚒𝚋/𝚜𝚎𝚌𝚞𝚛𝚒𝚝𝚢/𝚋𝚕𝚘𝚌𝚔𝚎𝚍.𝚌𝚎𝚛𝚝𝚜, a file that the JDK checks for every certificate in a chain. On October 23 he raised the list from 16 to 48 hashes, because the same certificate has several valid DER encodings. To check a JVM, OpenJDK publishes its own test, CheckCerts.java.

Three details from the OpenJDK page apply to more people than you would expect. JDKs from the Debian and Ubuntu packages need 𝚛𝚖 -𝚏 /𝚎𝚝𝚌/𝚜𝚜𝚕/𝚌𝚎𝚛𝚝𝚜/𝚓𝚊𝚟𝚊/𝚌𝚊𝚌𝚎𝚛𝚝𝚜 and 𝚞𝚙𝚍𝚊𝚝𝚎-𝚌𝚊-𝚌𝚎𝚛𝚝𝚒𝚏𝚒𝚌𝚊𝚝𝚎𝚜 --𝚏𝚛𝚎𝚜𝚑, because of “a long-standing bug” in 𝚌𝚊-𝚌𝚎𝚛𝚝𝚒𝚏𝚒𝚌𝚊𝚝𝚎𝚜-𝚓𝚊𝚟𝚊.

JDKs from Homebrew and FreeBSD use their own keystore and need a package update. And a GraalVM native image captures the trust store of the build machine at build time, per the GraalVM docs. A patched JDK on the server does not change a binary that is already built. You rebuild it.

Someone has already tested how broken the keys are. In the same Mozilla bug, Jacob Rakai counted 107,107 certificates signed with six of the eight keys. They appeared in public CT logs on October 20 and 21, after the revocation, and eMudhra issued none of them.

On October 25 eMudhra’s Scott Rea wrote: “We have taken the decision to exit the public TLS business”. The incident page collects the rest of the timeline.

Fitting end to that crazy story.


The regular October update came out the day before, and it was large too. Oracle’s Critical Patch Update of October 20 has “55 new security patches for Oracle Java SE. 49 of these vulnerabilities may be remotely exploitable without authentication”. The OpenJDK advisory lists 47 CVEs in OpenJDK and 6 in OpenJFX, fixed in 8u512, 11.0.33, 17.0.21, 21.0.13, 25.0.5 and 27.0.1. It has nothing to do with eMudhra. If you skipped the CPU because the eMudhra builds came after it, the .𝟶.𝟹 builds contain both.

The frameworks had the same kind of month. Michael Minella announced in September that Spring now releases the whole portfolio on one day, “the Thursday after the third Monday of the month”. The reason is in the post: “We have averaged nearly 80 new community provided security reports a month since March”, plus internal scanning with frontier models, and “over 160 new CVEs” fixed. October 22 was the first such train.

That’s not all - Quarkus shipped emergency releases 3.40.1.1, 3.33.4.1 and 3.27.6.1 on October 19 for CVEs in Netty, Vert.x, snappy-java and lz4-java. Micronaut 5.2.2 moved to Netty 4.2.19.Final for 16 Netty CVEs, including HTTP request smuggling.

And according to Michael Redlich‘s InfoQ roundup, Apache Camel fixed 38 CVEs, and Claude found 28 of them as part of Project Glasswing.

Spring’s post has the sentence that explains the month: “in a world where CVEs are exploited in hours instead of days, [old two-week release train] now seems archaic”.

That is time of a change for sure for industry.


TornadoVM deleted its copy of Graal. Quick recap: TornadoVM compiles Java methods into GPU kernels at runtime. JDK 27 removed JVMCI, the interface its compiler depended on, and in September TornadoVM 6.0 replaced JVMCI with a layer of reflection, ASM and 𝚄𝚗𝚜𝚊𝚏𝚎. That was a workaround. TornadoVM 8.0.0, released on October 26, removes the reason for it.

Quoting the post: “Since the beginning, TornadoVM has turned Java bytecode into GPU code through a vendored, relocated copy of the Graal compiler running on JVMCI. As of this release that compiler is gone.” The replacement is TC, TornadoVM’s own compiler, written in plain Java and running on JDK 21 and later. TC reads class files itself, without reflection or class loading. It builds an SSA intermediate representation, which uses block arguments instead of phi nodes, as MLIR does.

Then it runs the usual passes: constant folding, GVN, LICM, dead code elimination and unrolling. At the end it prints CUDA C++, OpenCL C or Metal source code, and the GPU driver compiles that. It needs no GPU to compile, so 𝚝𝚌 𝚎𝚖𝚒𝚝 produces a kernel on a laptop without a graphics card.

What’s interesting, TC passes more of the project’s test suite than Graal did, for example 1,539 tests against 1,516 on CUDA with an RTX 4090. The authors explain the difference: they are “kernels Graal refused and TC compiles”. On jitLLM, the project’s LLM engine, the output is token-identical and throughput is 1.06 to 1.14 times Graal’s on CUDA. The asterisk is Metal, which the team measured before the Graal code was deleted and “hasn’t been rerun since the removal”.

Two side effects interest me more than the speed. The first is that a generated kernel is readable. Variables keep their Java names, and every line of CUDA has a comment with the Java line it came from. A kernel that does not compile gets an error code and a Java line number, not a stack trace from inside a compiler phase.

The second is startup. JVMCI blocked both the Leyden AOT cache and GraalVM Native Image, and both work now. Take a saxpy program with one CUDA kernel on an RTX 4090. From launch to exit it takes about 0.6 s on a plain JVM, and about 0.3 s from an AOT cache or as a native image. The JNI-based libraries (cuDNN, cuDF, CUTLASS) do not work in Native Image yet.

TornadoVM speed is amazing in itself


JetBrains put IntelliJ’s Java engine into VS Code and Cursor, and the license is for humans.

Java and Kotlin by IntelliJ IDEA reached 1.0 on October 15, ten weeks after the preview, in a post by Marco Behler. It is an extension that talks to “IntelliJ IDEA’s Language Server and Debug Adapter”, so the editor is VS Code or Cursor and the code model behind it is IntelliJ’s. Version 1.0 supports Java, Kotlin and mixed projects, with Maven, Gradle and Bazel. It has debugging and JUnit as the only test runner. JetBrains recommends that you disable the Red Hat and Oracle Java extensions.

The free preview has also ended. The extension “requires an IntelliJ IDEA Ultimate subscription after a 30-day trial period”. The next sentence is the interesting one: “The license is explicitly designed for human use; we’ll release an update regarding licensing models for purely agentic use soon.” The post also says that agents will call the language server directly “in one of the next releases”. So the agent use case has a name, and its pricing is still to be announced.

On the same day the JetBrains AI blog showed that Netflix had already built the agent version. The case study, written by Anastasia Kazakova, starts with a problem stated by Netflix’s Paul Bakker: “Coding agents like Claude Code are slow and unreliable at navigating Java/Kotlin code, especially across library (JAR) boundaries.” Netflix took a headless build of the language server and wrote a daemon and a CLI around it, “infrastructure that JetBrains hadn’t shipped yet”. Both are in Java and ship as a GraalVM native executable. Claude Code and Codex call that CLI. Netflix had first tried the Eclipse language server, which “just didn’t work” with its Gradle-based build.

The benchmark ran in July and August on an internal Spring Boot 4 service and on the open-source DGS Framework. With the engine, agents were “typically 20–30% more efficient”, where efficiency is the average of token savings and time savings. The best case was about 65%, on code navigation with the strongest model at high effort. On end-to-end tasks the gains were 10% to 30%.

The study measured correctness with an LLM as the judge, and it “held steady or improved in every run”. As JetBrains’ Aleksey Stukalov said: “Agents need a code engine, not a virtual editor.”

In vol. 189 I wrote that Databricks and JetBrains both decided the language server is the product. October shows what follows from that: the model gets two customers, a person and an agent, and the agent apparently gets its own price list.

Smaller signs of the same thing came from elsewhere. Spock 2.5 ships “a Markdown version of the documentation for AI agents”. On jdk-dev, someone proposed better line editing for 𝚓𝚍𝚋, and Alan Bateman pointed the author to serviceability-dev, then added: “It might also be interesting to see how to make the command line experience more synchronous so that it can be driven by AI agents.”

Julien Dubois wrote up three Java Champions using GitHub Copilot: Vlad Mihalcea, Patrick Baumgartner and Frank Delporte. Baumgartner moved about 900 teaching slides from Reveal.js to Marp in eight to ten hours, and summarized the method in four words: “delegate execution, not responsibility.”


JDK 28 got four JEPs in October. JEP 543, Structured Concurrency by Alan Bateman, Viktor Klang and Ron Pressler, is now final, after seven previews. The text says it plainly: “We here propose to finalize Structured Concurrency in JDK 28, without further change.” If you remember 𝚂𝚑𝚞𝚝𝚍𝚘𝚠𝚗𝙾𝚗𝙵𝚊𝚒𝚕𝚞𝚛𝚎 and 𝚂𝚑𝚞𝚝𝚍𝚘𝚠𝚗𝙾𝚗𝚂𝚞𝚌𝚌𝚎𝚜𝚜 from older blog posts, those classes are gone. The final API opens a scope with 𝚂𝚝𝚛𝚞𝚌𝚝𝚞𝚛𝚎𝚍𝚃𝚊𝚜𝚔𝚂𝚌𝚘𝚙𝚎.𝚘𝚙𝚎𝚗() and chooses a policy with a 𝙹𝚘𝚒𝚗𝚎𝚛, such as 𝙹𝚘𝚒𝚗𝚎𝚛.𝚊𝚕𝚕𝚂𝚞𝚌𝚌𝚎𝚜𝚜𝚏𝚞𝚕𝙾𝚛𝚃𝚑𝚛𝚘𝚠(). Subtasks inherit 𝚂𝚌𝚘𝚙𝚎𝚍𝚅𝚊𝚕𝚞𝚎 bindings from the parent.

JEP 545 (Faster Startup and Warmup with ZGC) and JEP 546 (Adaptive Heap Sizing for ZGC), both owned by Erik Österlund, were targeted on October 15. And JEP 544, Ahead-of-Time Code Compilation by John Rose, was targeted at the end of September and is now integrated. It is the Leyden step that stores compiled native code in the AOT cache, next to the loaded classes and the profiles that JDK 24 and 25 added. On the JEP’s benchmarks, startup time drops by “around 65% to 80%” with AOT code, against 50% to 70% without it.

Mark Reinhold proposed the JDK 28 schedule on October 22: Rampdown Phase One on December 3 and GA on March 9, 2027. The first release candidate ships on a Tuesday, “to give the RC build three full weeks of testing exposure”. Also new is a candidate, JEP 547, Lazy Constants in its fourth preview, by Per-Ake Minborg.


A lap around the mailing lists.

On loom-dev, Arkadiusz Przychocki of Exeris wrote a custom scheduler for virtual threads on the Loom EA build. It had one queue per carrier thread and no work stealing. He measured it carefully: 10 JVMs per configuration, 228 trials, p99 latency under wrk2 load. With carriers pinned to CPU cores, p99 at 90% load dropped from 21.39 ms to 2.58 ms.

Then he did what few benchmark authors do and tested the boring version too. The stock ForkJoinPool, with its workers pinned, matched or beat his scheduler. His October 23 follow-up says it in one line: “in these runs the gain was the pinning”. Alan Bateman answered his questions about the scheduler contract and gave the shortcut: write a 𝙵𝚘𝚛𝚔𝙹𝚘𝚒𝚗𝚆𝚘𝚛𝚔𝚎𝚛𝚃𝚑𝚛𝚎𝚊𝚍𝙵𝚊𝚌𝚝𝚘𝚛𝚢 that pins threads and pass it with -𝙳𝚓𝚍𝚔.𝚟𝚒𝚛𝚝𝚞𝚊𝚕𝚃𝚑𝚛𝚎𝚊𝚍𝚂𝚌𝚑𝚎𝚍𝚞𝚕𝚎𝚛.𝚝𝚑𝚛𝚎𝚊𝚍𝙵𝚊𝚌𝚝𝚘𝚛𝚢.

On valhalla-dev, the topic was literals. 𝙸𝚗𝚝𝚎𝚐𝚎𝚛 𝚒 = 𝟷𝟹; compiles, but a value class you write yourself cannot be assigned from a number like that. Saurav Bhattarai proposed an 𝚘𝚙𝚝 modifier for one-argument constructors, so that 𝙸𝚗𝚝𝟷𝟼 𝚊 = 𝟷𝟹; would compile. Dan Smith replied that the team is considering “user-defined declarations of pairs of convertible types”, and Brian Goetz added: “you will have to be patient a bit longer”. The Vector API got the same answer in September.

On leyden-dev, a report from October 24 found that an AOT training run calls 𝚜𝚝𝚊𝚝() on every classpath entry for every loaded class. On Linux with 400 JARs, that cost 13.2 s against 0.48 s. With a local memoization fix, class loading during training went from 7.5 s to 0.78 s. Nobody has replied yet, but if your AOT training takes minutes on a large classpath, now you know where to look.

On panama-dev, the topic was vector masks, the sets of bits that tell an operation which lanes of a vector to use. 𝚅𝚎𝚌𝚝𝚘𝚛𝙼𝚊𝚜𝚔.𝚝𝚘𝙻𝚘𝚗𝚐() packs a mask into one 𝚕𝚘𝚗𝚐, so it throws an exception when a vector has more than 64 lanes, which can happen with ARM SVE. Arm’s Stuart Monteith continued the thread at the end of September. After a suggestion from Paul Sandoz, the proposal is now 𝚏𝚛𝚘𝚖𝙼𝚎𝚖𝚘𝚛𝚢𝚂𝚎𝚐𝚖𝚎𝚗𝚝 and 𝚒𝚗𝚝𝚘𝙼𝚎𝚖𝚘𝚛𝚢𝚂𝚎𝚐𝚖𝚎𝚗𝚝 on masks.

And on amber-dev, someone asked Java to relax one of its small rules. Today you cannot declare a local variable with the same name as another local variable that is still in scope. An 𝚒𝚗𝚝 𝚗 before a block and another 𝚒𝚗𝚝 𝚗 inside it is a compile error, “variable is already defined”. C, JavaScript and Rust allow it, and the name for it is shadowing. The “JEP Draft” proposed allowing it on purpose, with a new 𝚜𝚑𝚊𝚍𝚘𝚠 keyword or a 𝚕𝚘𝚌. prefix, and it got a long answer from Brian Goetz. His point is that we only notice the rule when it gets in our way: “we are much more aware of the times where we were stopped from using shadowing … than we are of the times where the rules against shadowing quietly saved us”. He did admit one exception, lambda parameters, where “we went overboard”.


Quarkus went to the desktop and made its builds repeatable. Quarkus Desktop 0.1.0, by Fouad Almalki, is a Quarkiverse extension for Swing and AWT applications. In its model, “a window is a CDI bean”, and “there is no main method and no @QuarkusMain”. You observe 𝙳𝚎𝚜𝚔𝚝𝚘𝚙𝚂𝚝𝚊𝚛𝚝𝚞𝚙𝙴𝚟𝚎𝚗𝚝, which fires on the Event Dispatch Thread, and 𝙴𝚍𝚝𝙴𝚡𝚎𝚌𝚞𝚝𝚘𝚛 moves work from background threads back to it. The build rejects @𝙰𝚙𝚙𝚕𝚒𝚌𝚊𝚝𝚒𝚘𝚗𝚂𝚌𝚘𝚙𝚎𝚍 on Swing components, because the CDI client proxy would be a second component, created outside the EDT. The main point is Native Image. The extension registers what AWT, Java2D, fonts, printing, the clipboard and Swing need in a native executable, and copies the JDK libraries that it loads. On macOS, native executables need Quarkus 4.0 and GraalVM 25.1 or later.

Quarkus Insights #263, with Holly Cummins hosting Teymur Babayev, Guillaume Smet and Ladislav Thon, was about byte-for-byte reproducible builds. The causes were ordinary: multi-build items without a stable sort order, a static 𝚌𝚘𝚞𝚗𝚝𝚎𝚛++ in recorder proxies, unordered collections, the order of the Jandex index. The first check failed roughly 2,000 tests. A new flag, -𝙳𝚚𝚞𝚊𝚛𝚔𝚞𝚜.𝚒𝚗𝚝𝚎𝚛𝚗𝚊𝚕.𝚝𝚎𝚜𝚝.𝚛𝚎𝚙𝚛𝚘𝚍𝚞𝚌𝚒𝚋𝚒𝚕𝚒𝚝𝚢-𝚌𝚑𝚎𝚌𝚔=𝚝𝚛𝚞𝚎, runs augmentation three times in one JVM and compares the results, and the nightly results are on status.quarkus.io. The team also said that an absolute guarantee is “virtually impossible”.


Two people from the Java world started new things. Emmanuel Bernard, known for Hibernate and Quarkus, announced Hadronic at the end of September: “A few months ago I co-founded a company in stealth, where I serve as CTO”. Hadronic AG is in Zug, and the site says “Where Java meets AI. Open by design.” The blog post adds “Sovereignty as a design goal, not a compliance checkbox”, and that the founders “got back together” from the open-source Java world. There is no product yet, so this is a name to remember, not something to try.

Graeme Rocher introduced Pyronaut on October 2. It runs Python code on GraalPy inside Micronaut, with build-time dependency injection. He reports “2.6 times the throughput of FastAPI and 6.5 times that of Flask”, and adds the caveat himself: it is “not yet comparable to Micronaut with Java”, because the Python code still runs behind the GIL.


Three to close.

Petr Pravda introduced Jax4j, an Apache 2.0 tensor library modeled on JAX, with automatic differentiation and GPU engines called through the FFM API. The repository was created on October 18, and its README says CUDA, ROCm and SIMD “remain unverified for this version”. Watch it, but do not build on it yet.

Alina Yurenko‘s Devoxx talk GraalVM Next: Layers, Crema, and WebAssembly in Action covers Native Image Layers, where the JDK and framework code go into a base layer that many images reuse, then Project Crema and WebAssembly support. It runs 47 minutes, with demos.

And Josh Long asked Andrew Lombardi on A Bootiful Podcast whether Java can outperform Rust. Lombardi used AI to port a real application to Kotlin, to compare it with the Rust rewrite everyone talks about. Listen for the part about benchmarking mistakes.

2. Release Radar

Apache Grails 8.0.0

Grails 8.0.0, announced by James Daugherty on October 8, is a baseline release: Java 21, Gradle 9.8, Groovy 5.1, Spring Boot 4.1, Spring Framework 7.0, Spring Security 7.1, Jackson 3 and Tomcat 11. Note that it is Groovy 5, not Groovy 6. GORM for Hibernate 7 arrives, GORM for Neo4j and Undertow come back, and SiteMesh 3 is the default layout engine. The guide documents training a JDK AOT cache, and the example startup goes from 2.594 s to 0.933 s.

The breaking changes are a long list, so read the upgrade guide. The Spring Dependency Management plugin is gone. @𝙴𝚗𝚊𝚋𝚕𝚎𝚆𝚎𝚋𝙼𝚟𝚌 is no longer added for you. 𝚍𝚘𝚆𝚒𝚝𝚑𝚂𝚙𝚛𝚒𝚗𝚐 is deprecated in favor of 𝚋𝚎𝚊𝚗𝚁𝚎𝚐𝚒𝚜𝚝𝚛𝚊𝚛(). Tests use @𝙼𝚘𝚌𝚔𝚒𝚝𝚘𝙱𝚎𝚊𝚗 instead of @𝙼𝚘𝚌𝚔𝙱𝚎𝚊𝚗.

The roadmap has an unusual line. Grails 9, with Groovy 6 and invokedynamic on, is planned for the end of November, so “there will be no 8.1”. Grails 10 follows in Q1 2027 on Spring Boot 4.2.

Release blog

Quarkus 3.40 LTS and Quarkus 4.0.0.Beta1

Quarkus 3.40, from Guillaume Smet on September 30, is the new LTS, supported for 12 months. It is “the direct continuation of 3.39”, and the migration guide “is empty”. The previous LTS is 3.33, and 3.27 got its last maintenance release.

A day later Smet announced Quarkus 4.0.0.Beta1. The final release is planned for the end of November. The list is long: Java 21 as the minimum, Netty 4.2 and Vert.x 5, opt-in HTTP/3 over QUIC, Hibernate ORM 8 with Jakarta Persistence 4.0, Jackson 3, CDI 5.0 and an @𝙴𝚡𝚊𝚌𝚝𝚕𝚢𝙾𝚗𝚌𝚎 annotation for Kafka. The post asks extension authors to migrate now and users to test their applications, so this is the month to try the beta on a branch.

Release notes

The Spring train of October 22

The first single-day train brought release candidates for the next minor versions. Spring Boot 4.2.0-RC1, announced by Moritz Halbritter, has 113 changes. The default Buildpacks builder is now based on Ubuntu 26.04. 𝚜𝚙𝚛𝚒𝚗𝚐-𝚋𝚘𝚘𝚝-𝚙𝚛𝚘𝚙𝚎𝚛𝚝𝚒𝚎𝚜-𝚖𝚒𝚐𝚛𝚊𝚝𝚘𝚛 can fail the application on errors or warnings, not only log them. And startup is faster, with quicker class and resource lookup in executable JARs. Spring Framework 7.1.0-RC1 removes APIs that were deprecated for removal, so compile against it before GA.

Spring AI 2.1.0-RC1, from Eric Bottard, adds message parts that work across providers, session memory and confirmation before a tool call runs. Spring Security 7.2.0-RC1 and Spring Cloud 2026.0.0-RC1 “Paddington” came in the same train, along with patch releases for Boot 4.1.2 and 4.0.9.

Spring Boot 4.2 release notes

Spock 2.5

Spock 2.5 came out on October 6 with support for Groovy 6 and Spring 7. 𝚠𝚑𝚎𝚛𝚎: blocks accept final local variables, and 𝚃𝚘𝚘𝙼𝚊𝚗𝚢𝙸𝚗𝚟𝚘𝚌𝚊𝚝𝚒𝚘𝚗𝚜𝙴𝚛𝚛𝚘𝚛 gives a better message. Three changes can break existing specs. An interaction on a 𝚗𝚞𝚕𝚕 target now throws 𝙸𝚗𝚟𝚊𝚕𝚒𝚍𝚂𝚙𝚎𝚌𝙴𝚡𝚌𝚎𝚙𝚝𝚒𝚘𝚗. Interactions declared outside 𝚝𝚑𝚎𝚗: stay active during cleanup. And on Groovy 6, a top-level arrow 𝚜𝚠𝚒𝚝𝚌𝚑 is “no longer checked” as a condition. The documentation is now also available in Markdown, for AI agents.

Release notes

LangChain4j 1.21.0 and 1.22.0

LangChain4j 1.21.0, from Dmytro Liubarskyi on October 2, introduces decision models. A decision model answers questions about an input “with typed answers and probabilities instead of generated text”: yes or no, a choice, a scale. Decision Services let you declare those questions as methods of a Java interface, the same way you declare AI Services. LangChain4j then maps the answers to 𝚋𝚘𝚘𝚕𝚎𝚊𝚗, enums or records. On top of that come a 𝚁𝚘𝚞𝚝𝚒𝚗𝚐𝙲𝚑𝚊𝚝𝙼𝚘𝚍𝚎𝚕 that picks the right chat model per request, re-ranking for RAG, tool selection and guardrails. All of it is @𝙴𝚡𝚙𝚎𝚛𝚒𝚖𝚎𝚗𝚝𝚊𝚕. The same release adds a jitLLM integration, the successor of GPULlama3.java, by Michalis Papadimitriou.

Version 1.22.0, six days later, adds support for the OpenAI Decisions API, batch inference on AWS Bedrock, and automatic prompt caching with a cache TTL for Anthropic models.

Release notes

WildFly 42

WildFly 42.0.0.Final came out on October 22, on WildFly Core 34. The feature that matters for most people is virtual threads in the Jakarta Concurrency 3.1 implementation (WFLY-19563). Idle-time eviction for distributable HTTP sessions, stateful session beans and timers moves to default stability. The bug fixes include two CWE-611 issues, XML external entity processing in timer persistence and in deployment descriptor parsing.

Release notes

Maven 3.10.0

While Maven 4 is still in release candidates, Maven 3.10.0 came out on October 1, on Resolver 2.0.21. It makes model validation stricter, with a new switch to control it, and it validates 𝚛𝚎𝚕𝚊𝚝𝚒𝚟𝚎𝙿𝚊𝚝𝚑. In 𝚜𝚎𝚝𝚝𝚒𝚗𝚐𝚜.𝚡𝚖𝚕 you can now define aliases for existing server configurations. A server entry can also declare which repository origins may use its credentials. So the password for the company Nexus does not go to a repository you did not intend. 𝚓𝚟𝚖.𝚌𝚘𝚗𝚏𝚒𝚐 now supports 𝙼𝙰𝚅𝙴𝙽_𝙿𝚁𝙾𝙹𝙴𝙲𝚃𝙱𝙰𝚂𝙴𝙳𝙸𝚁. The stricter validation may report problems that older versions ignored, so run the build in CI before you upgrade developer machines.

Release notes

Elide 1.6.0

Elide 1.6.0 came out on October 16. Elide is one runtime and toolchain for Kotlin, JavaScript, TypeScript and Python, and it also builds and tests Java. For JVM projects, the build side of this release matters most. Workspaces add multi-module projects: you list the members in the root 𝚎𝚕𝚒𝚍𝚎.𝚙𝚔𝚕 and build or test all of them with one command. 𝚙𝚛𝚘𝚓𝚎𝚌𝚝(”𝚌𝚘𝚛𝚎”).𝚏𝚛𝚒𝚎𝚗𝚍() gives a member access to the Kotlin 𝚒𝚗𝚝𝚎𝚛𝚗𝚊𝚕 declarations of a sibling. 𝚎𝚕𝚒𝚍𝚎 𝚙𝚞𝚋𝚕𝚒𝚜𝚑 builds the JARs and the POM and sends them to the local Maven repository, to Nexus or Artifactory, or to Maven Central, with checksums and signatures. 𝚎𝚕𝚒𝚍𝚎 𝚛𝚎𝚠𝚛𝚒𝚝𝚎 runs OpenRewrite recipes without a build plugin. It is experimental, and the team plans compatibility fixes for 1.6.1. Smaller additions: Java annotation processors, JaCoCo coverage from 𝚎𝚕𝚒𝚍𝚎 𝚛𝚞𝚗 --𝚌𝚘𝚟𝚎𝚛𝚊𝚐𝚎, and a build cache that several machines can share. Python is now an optional component, which you add with 𝚎𝚕𝚒𝚍𝚎 𝚜𝚎𝚝𝚞𝚙 𝚙𝚢𝚝𝚑𝚘𝚗. Elide also publishes its own benchmarks, in which a JavaScript hello-world starts in 1.43 ms, against 1.63 ms for Bun and 11.7 ms for Node.js. Those are the vendor’s numbers from one machine, so measure your own workload before you quote them.

Release notes

3. GitHub All-Stars

Seven this month, plus two bonus rounds at the end. Three of them are a postscript to vol. 196 about Minecraft, because the community did not stop after I published it.

minecraft-metal-shaders, a Metal backend through FFM

minecraft-metal-shaders by Gael Quintanilla is a Fabric mod that gives Minecraft Java 26.3 a native Metal renderer on macOS. The README starts with the motivation: “Every shader pack for Java Edition runs through Iris on OpenGL, and OpenGL on macOS has been frozen since 2011.”

The JVM part is the clean part. 𝙼𝚎𝚝𝚊𝚕𝙱𝚊𝚌𝚔𝚎𝚗𝚍 implements Mojang’s own 𝙶𝚙𝚞𝙱𝚊𝚌𝚔𝚎𝚗𝚍 interface from the 𝚌𝚘𝚖.𝚖𝚘𝚓𝚊𝚗𝚐.𝚛𝚎𝚗𝚍𝚎𝚛𝚙𝚎𝚊𝚛𝚕 package, the same one as the OpenGL and Vulkan backends. The Java side calls a native 𝚕𝚒𝚋𝚖𝚌𝚖𝚎𝚝𝚊𝚕.𝚍𝚢𝚕𝚒𝚋 through the FFM API, and it links the encoder calls as critical downcalls. The comment explains the reason: “no thread-state transition, so a draw costs a few nanoseconds of glue”. That is the most practical example of 𝙻𝚒𝚗𝚔𝚎𝚛.𝙾𝚙𝚝𝚒𝚘𝚗.𝚌𝚛𝚒𝚝𝚒𝚌𝚊𝚕() I have seen this year. It requires Java 25.

It is a credited fork of noahdunnagan/mcopt under Apache 2.0, labeled alpha, and most commits have Claude as co-author. The FPS numbers are from one M4 machine, the author’s own.

Horizon, Minecraft in Rust with vanilla as the referee

Horizon by Ludovic Marie reimplements Minecraft Java 26.3 in Rust. It passed 2,600 stars in about three weeks. It also has 6,483 commits in 24 days, and Claude, Codex and Devin are co-authors on most of them. The README is careful about the legal side: “Horizon contains no Minecraft code, assets or data: you need your own legally obtained copy of Minecraft: Java Edition 26.3.”

It is in a JVM newsletter for its test setup, and the setup is Java. To check parity, Horizon runs 281 “differential harnesses” against the real game. The class 𝙿𝚛𝚎𝚙𝚊𝚛𝚎𝚆𝚘𝚛𝚔𝚎𝚛 boots vanilla Minecraft once and takes tasks as JSON lines on stdin. It runs each task by reflection and restores the registry tags afterward, so one warm JVM serves as the reference for many comparisons. Building it needs JDK 25.

Keep the claims in proportion. The README badge says “vanilla parity 100%”, and the same README says that Java mods do not run. The parity counts what the harnesses check, and running mods is not part of it.

RTX Remix for Minecraft, path tracing from NVIDIA

rtx-remix-minecraft comes from NVIDIA itself. It is a Fabric mod for Minecraft Java 1.21.1 that hands the scene to RTX Remix and renders it with real-time path tracing. It needs Windows and a GeForce RTX 30-series card or newer, and it is labeled beta. The README is unusually frank: the mod was tuned “against one showcase world”, built for the project by BlockWorks, and other mods, a full playthrough and other versions “have had less attention than the rest”.

The JVM part takes the opposite route from minecraft-metal-shaders above. There is no FFM here. 𝚁𝚎𝚖𝚒𝚡𝙱𝚛𝚒𝚍𝚐𝚎 is a classic JNI class in which “every method maps to a single 𝙹𝙽𝙸𝙴𝚇𝙿𝙾𝚁𝚃 in 𝚛𝚎𝚖𝚒𝚡𝚋𝚛𝚒𝚍𝚐𝚎.𝚍𝚕𝚕”, a one-file C++ shim over the Remix C API. One comment in that file explains why the shim links OpenGL itself: “LWJGL does not expose” a semaphore function it needs.

The detail I liked is about security. The Minecraft launcher passes your account to the game on the command line (--𝚊𝚌𝚌𝚎𝚜𝚜𝚃𝚘𝚔𝚎𝚗, --𝚞𝚞𝚒𝚍 and three more), and the Remix runtime copies the command line into its crash dumps. So before the runtime loads, 𝙲𝚘𝚖𝚖𝚊𝚗𝚍𝙻𝚒𝚗𝚎𝚁𝚎𝚍𝚊𝚌𝚝𝚒𝚘𝚗 overwrites those values with asterisks in the process memory. It keeps the length of the line and the way it splits into arguments. A crash report from a player then does not carry their session token.

OpenTRAP, a Burp-style workspace in Java on top of ZAP

OpenTRAP is a Swing application, 98% Java under AGPL-3.0, that offers “the familiar Burp-style workflow on top of the trusted OWASP ZAP detection engine”. You get an intercepting proxy, Repeater, Intruder and an out-of-band collaborator. The proxy has its own man-in-the-middle CA built with BouncyCastle. ZAP itself runs headless and is driven over its REST API.

The detail I liked is in 𝚉𝚊𝚙𝙴𝚗𝚐𝚒𝚗𝚎. ZAP runs as a separate process, so if the JVM crashes, ZAP and its browsers can stay alive as orphans. OpenTRAP starts ZAP through a small shell watchdog and keeps a pipe to its stdin open, and it never writes to that pipe. When the JVM dies for any reason, including 𝚔𝚒𝚕𝚕 -𝟿, the pipe closes and the watchdog reads EOF. Then it shuts ZAP down cleanly through the API and kills any leftover Selenium browsers. No shutdown hook can do that, because a JVM killed with 𝚔𝚒𝚕𝚕 -𝟿 runs no hooks.

My caveats: the organization was created on October 19, and its profile names no maintainer. The repository got almost 600 stars in eight days, and I could not check where they came from. It runs on Linux only. The code is readable, so read it before you point it at anything.

Vitro, Liquid Glass for Jetpack Compose

Vitro by Hedi Missaoui describes itself as “Apple’s Liquid Glass for Android”. It is a Compose library on Maven Central, with an API modeled on SwiftUI’s 𝚐𝚕𝚊𝚜𝚜𝙴𝚏𝚏𝚎𝚌𝚝. On Android 13 and later it renders the glass with an AGSL shader, Android’s shading language. Older versions fall back to platform blur or a blurred snapshot.

Two details show that someone thought about performance. One AGSL shader takes up to eight shapes as uniform arrays and merges them with a rounded-rectangle distance function. So a group of glass elements is drawn in one pass, not eight. And the background is read through a private 𝙷𝚊𝚛𝚍𝚠𝚊𝚛𝚎𝚁𝚎𝚗𝚍𝚎𝚛𝚎𝚛 into an 𝙸𝚖𝚊𝚐𝚎𝚁𝚎𝚊𝚍𝚎𝚛 on a separate thread, so the render thread does not stall. The repository is days old, with 22 stars and no AI co-author trailers in the history.

ktrs, ktfmt and ktlint without a JVM

ktrs by Hexay rewrites ktfmt and ktlint in Rust. The first line of the README is the pitch: “Kotlin formatting and linting in Rust, without starting a JVM.” In an editor or a pre-commit hook, a formatter runs on one file, and JVM startup, “roughly a second”, is most of the cost. ktrs claims under 10 ms per file, and 13 to 190 times the speed of the originals.

Speed is the easy part. Compatibility is the hard one, and the README puts numbers on it: output byte-identical to ktfmt 0.65 on 6,121 of 6,123 real-world files, and lint results that match ktlint 2.0 in all three code styles. Under it is a lossless Kotlin parser “whose tree matches the Kotlin compiler’s PSI node for node”. The 𝚔𝚝𝚏𝚖𝚝 and 𝚔𝚝𝚕𝚒𝚗𝚝 binaries accept the original flags and exit codes, so a CI script does not need to know that the tool changed. These are the author’s own measurements, the repository is a month old, and it has one human contributor. Run it next to ktfmt on your own code before you switch.

midden, heap dumps without a JDK

midden by NullByte3 applies the same idea to heap dumps: “Find what is holding a JVM heap dump’s memory: retained sizes, owners, leak suspects. One static binary, no JDK needed.” It reads .𝚑𝚙𝚛𝚘𝚏 files from 𝚓𝚌𝚖𝚍, 𝚓𝚖𝚊𝚙 or -𝚇𝚇:+𝙷𝚎𝚊𝚙𝙳𝚞𝚖𝚙𝙾𝚗𝙾𝚞𝚝𝙾𝚏𝙼𝚎𝚖𝚘𝚛𝚢𝙴𝚛𝚛𝚘𝚛, builds the dominator tree and prints the leak suspects. If you have ever opened a 1 GB dump in Eclipse MAT and waited, that is the tool it competes with.

The flags show that the author debugs real leaks. --𝚌𝚕𝚊𝚜𝚜 𝙾𝚋𝚓𝚎𝚌𝚝𝙼𝚊𝚙𝚙𝚎𝚛 lists who holds that class, --𝚏𝚒𝚗𝚍 𝚓𝚍𝚋𝚌:𝚙𝚘𝚜𝚝𝚐𝚛𝚎𝚜𝚚𝚕 finds strings and their owners, and --𝚋𝚊𝚜𝚎𝚕𝚒𝚗𝚎 𝚋𝚎𝚏𝚘𝚛𝚎.𝚑𝚙𝚛𝚘𝚏 shows what grew between two dumps. The author’s benchmark puts midden at 8 to 23 times faster than MAT on the same dumps, for example 1.30 s against 20.95 s for a 938 MB dump of a Paper server. It needs about 90 bytes of RAM per object, and it reports sizes the way MAT does by default. Rust, MIT, 22 commits, one author.

Bonus: the Android TV wave

Not a JVM story, but it is Java and Kotlin, and it happened in two weeks. Between October 15 and 28, 33 new Java and Kotlin repositories about Android TV or Google TV passed 50 stars on GitHub. Most of them are a home screen plus a debloat script, built with a coding agent and installed by one.

The biggest is Free My TV (5,159 stars, MIT, Java), “a fast, ad-free home screen for Google TV, LG and Samsung TVs”. It says “Built with Claude Code”, and the recommended install is to tell Claude Code, Codex or Antigravity: “Install Free My TV (github.com/simbasjungles/free-my-tv) on my TV at 192.168.1.50.” The author measured the home screen at 96 MB of memory against 242 MB for the stock one on a TCL Google TV, and one button restores the factory launcher. TV Detox (1,705 stars) sums up the method: “Claude Code or Codex does the work. You just press OK on the remote.” The Verge wrote about the trend on October 23. aerial-tv (7,300 stars, Kotlin) does the same for Xiaomi and TCL TVs sold in China.

What interests me is the delivery model. The install section of these READMEs is not written for a person anymore. It is a prompt, and the person’s job is to press OK on the remote.

Bonus 2: reverse engineering, in Kotlin and Java

The other trend of the month gets only a mention, because these projects sit in a legal grey zone. Three of the ten most-starred new Kotlin repositories since late September are DiPlay (12,668 stars), an independent CarPlay receiver for Android head units, and two of its forks. CarDeck goes the other way and makes an Android phone act as an iPhone for CarPlay. Open Madrid reimplements Apple’s iMessage protocol in Kotlin, with the registration chain “verified against Apple”.

Crazy times


PS: The panama-dev thread about String processing and the Vector API from vol. 189 has moved again. On October 14 Liam Miller-Cushon refined his proposal in PR #32373 and rewrote the description, which now also discusses alternatives, “including a public MemorySegment view”. Nobody has replied yet.

PS2: In September Guillaume Nodet wrote that Maven 4.0.0-rc-7 was “expected to be the last before the Maven 4.0.0 GA release, which we aim to publish in the coming weeks”. It is still the latest tag, five weeks later. The weeks are still coming, and Maven 3.10.0 is above in the meantime.

Thanks for reading JVM Weekly!

Thanks for reading JVM Weekly! Subscribe for free to receive new posts and support my work.

参与讨论