JVM platform engineer. Language runtimes, module systems, and the tooling other developers build on.
Cloud Manager at Enonic. For the last seven years I've worked on Enonic XP — an open-source content platform built on Java, OSGi and Elasticsearch, which executes application code written in JavaScript inside the JVM.
XP's server-side JavaScript ran on Nashorn. I've been moving it to GraalJS: embedding the engine, managing polyglot context lifecycle, keeping the module semantics that existing application code depends on, and then carrying the whole library and application ecosystem across.
- Server-side rendering on GraalJS (2022) — react4xp SSR, the first production surface to move off Nashorn.
- GraalJS Preview (2026) — the engine at platform level in XP 8.
- ~30
GraalJS Compatibilitypull requests acrosslib-sql,lib-cron,lib-cache,lib-xslt,app-contentstudio,app-users,app-explorerand the rest of the ecosystem. The unglamorous half of a runtime migration, and the half that decides whether it ships.
- GraalVM 22.2 gained the ability to set
polyglot.js.intl-402throughScriptEnginebindings. The first answer was no — use a system property instead. But a system property is global, and XP loads and reloads engines concurrently under OSGi, so the suggested workaround races. That argument changed the answer and the option shipped. - JDK 20 got idle-connection timeouts for the
HTTP/2 connection pool, from a report I filed. Cloud load balancers silently drop idle TCP
connections before the server sends GOAWAY, so
HttpClientfails on reuse with "connection reset by peer". Shipped with a release note and a CSR.
I handle security reports for XP: triage, reproduction, patching, and advisory publication.
CVE-2024-23679 — session
fixation in lib-auth, rated Critical — came in as a customer report and went out as a published
advisory with a workaround and a patched release.
PropertyTree was using more heap than it should, so the node cache got its own representation
(f89e453).
Cached node versions are held in an arity-specialised immutable form — separate classes for
single- and multi-valued properties, the way java.util.ImmutableCollections splits List12
from ListN — and materialise into a PropertyTree only when something asks for one. Shared
sentinels for empty and null sets, canonical instances for the common NodeType, and Jackson
mixins so the compact types deserialize straight off the blob stream without annotating a single
domain class.
Related work in the same vein:
- Heap accounting for immutable structures — retained size of
Map.of/List.ofgraphs, including per-string overhead and hash-table slack. - Compact object headers — verifying the runtime flag through
HotSpotDiagnosticMXBeanand cross-checking real footprint with JOL. - Caching — XP's caches moved onto Caffeine; read-through blobstore bounded by a W-TinyLFU index.
I wrote Booster, XP's response cache, from the initial
implementation onward: cache keys and component-level caching, invalidation on publish and on
expiry, per-site TTL, cleanup that won't run twice at once, Accept-Encoding handling, and audit
logging for manual purges. Caching is easy until invalidation; most of the work is invalidation.
- Platform internals — OSGi module system and classloading, content repository on Elasticsearch, permissions model, versioning and branch semantics.
- Developer tooling — Gradle plugins, CLI, build and release pipelines, GitHub Actions.
- Production — XP as a managed cloud service, and the team that runs it.
- XP 8 – Sneak Peek — Enonic developer webinar. The Universal API in the next major version of XP.
- Build, Test, and Release Enonic Apps Using GitHub Actions — Enonic developer webinar. CI/CD for platform apps and libraries.
Home-automation tinkering in Python — that's what the forks on this profile are, not the day job.
Java · Kotlin · JavaScript on the JVM · GraalVM · OSGi · Elasticsearch · Gradle




