This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
mvn clean package— full build; produces the shaded kernel jar and the kernelspec zip underjjava-distro/target/.mvn clean verify— what CI runs on Linux/macOS. Adds Failsafe integration tests (the*IT.javacases injjava-distro, which spin upeclipse-temurincontainers via Testcontainers). Testcontainers does not work on Windows; CI usesmvn clean testthere.mvn test -pl jjava-kernel -am— single-module unit tests (with required upstream modules built).mvn -pl jjava-kernel test -Dtest=JavaKernelExtensionsLifecycleTest— run a single test class.- Surefire in
jjava-kernelandjjava-distrois configured with--add-opens jdk.jshell/jdk.jshell=ALL-UNNAMED— required for JShell reflection. Anything that exercisesJavaKernel/CodeEvaluatordirectly needs the same flag. - Local install of the freshly built kernel into Jupyter: see
DEV-GUIDE.md(unzipthe kernelspec zip, thenjupyter kernelspec install ... --name=java --user).
Minimum toolchain: JDK 11 (maven.compiler.release=11). CI matrix builds against 11, 17, 21, 25.
This is a multi-module Maven project (groupId: org.dflib.jjava). Each module has a distinct role — do not collapse them.
jjava-jupyter ← generic, language-agnostic Jupyter kernel framework
▲
jjava-kernel ← Java-specific kernel (uses JShell), built on jjava-jupyter
▲
jjava-maven ← Maven dependency resolution magics, built on jjava-kernel
▲
jjava-distro ← Fat/shaded jar combining kernel + maven + magics. THE deliverable.
jjava-launcher ← Tiny no-dependency jar; spawns the child java process for jjava-distro.
jjava-jupyter— Reusable JVM Jupyter library: the ZMQ wire protocol (jeromq), message types and Gson adapters, channel loops (shell,iopub,stdin,heartbeat,control), display/rendering, magics infrastructure (MagicsRegistry,MagicsResolver,MagicTranspiler), comm, history, andBaseKernel— the abstract superclass that any language-specific kernel extends. Published to Maven Central as a library for building other JVM kernels.jjava-kernel—JavaKernel extends BaseKernel. Owns theJShellinstance andCodeEvaluator(with customJJavaExecutionControlandJJavaLoaderDelegate). Ships built-in magics underorg.dflib.jjava.kernel.magics:%load,%classpath,%time,%jars(deprecated).jjava-maven— Maven dependency resolution, isolated so the core kernel stays slim. Uses Maveniverse Mima for resolution. Ships%maven,%mavenRepo,%loadFromPOM(line + cell),%addMavenDependency(deprecated).jjava-distro— The actual runnable kernel jar. Main class:org.dflib.jjava.distro.JJava. Wires all magics intoJavaKernel.builder(), readsJJAVA_*env vars (seeEnv.java), parses the Jupyter connection file, and runs the message loop. Usesmaven-shade-pluginto relocate every 3rd-party package (gson, jeromq, slf4j, maven, mima, etc.) underorg.dflib.jjava.shaded.*— keep this in mind when reading stack traces from a built kernel. SLF4J is bound toslf4j-jdk14(JUL) inside the shaded jar.jjava-launcher— Standalone jar with zero dependencies. Spawns a childjavaprocess running the distro jar, applying--add-opens jdk.jshell/jdk.jshell=ALL-UNNAMEDand anyJJAVA_JVM_OPTS. Itspackagephase also copies the launcher jar intokernelspec/java/jjava-launcher.jar. Usesjava.util.loggingdirectly rather than SLF4J because it must remain dependency-free.
kernelspec/java/kernel.json is the Jupyter kernel descriptor — its argv invokes jjava-launcher.jar, which in turn invokes jjava.jar. The shade output of jjava-distro and the jjava-launcher jar are both copied into the assembled jjava-<version>-kernelspec.zip (see jjava-distro/assembly/zipped-kernel.xml).
pip/jjava/ and pyproject.toml package the kernelspec for PyPI distribution; version is injected from the git tag by the release workflow.
- JShell-based execution. All user code runs in a
JShellinstance owned byJavaKernel.CodeEvaluatororchestrates snippet evaluation, andJJavaLoaderDelegateparticipates in JShell's class loading. Adding to the classpath at runtime means callingkernel.addToClasspath(...), which both extends JShell's classpath and triggers extension discovery from the new entries. - Magic resolution happens before evaluation. Cells are passed through
MagicTranspiler/MagicsResolver. Line magics start with%, cell magics with%%. New magics are registered inJJava.main()via.lineMagic(...)/.cellMagic(...)on theJavaKernel.Builder. - Extensions are discovered via
ServiceLoaderfororg.dflib.jjava.jupyter.Extension, scanning both the application classpath and any classpath added at runtime through magics.JJAVA_LOAD_EXTENSIONS=falsedisables this entirely. Extensions get aninstall(BaseKernel)callback on load anduninstallon shutdown. - Environment variables consumed by the kernel are centralized in
org.dflib.jjava.distro.Env:JJAVA_COMPILER_OPTS,JJAVA_TIMEOUT,JJAVA_CLASSPATH,JJAVA_STARTUP_SCRIPT,JJAVA_STARTUP_SCRIPTS_PATH,JJAVA_LOAD_EXTENSIONS.JJAVA_JVM_OPTSis read by the launcher, not the kernel, and is not forwarded to the child process beyond being unpacked into the JVM command line. - Logging. Code in
jjava-jupyter,jjava-kernel,jjava-mavenuses SLF4J. The launcher andJJavamain class use JUL directly (the launcher because it has no dependencies;JJava.mainbecause it sets the JULSimpleFormatter.formatsystem property to mimic Jupyter's log style before the SLF4J→JUL bridge takes over). In the shaded distro jar, SLF4J is itself relocated toorg.dflib.jjava.shaded.org.slf4jand routed to JUL viaslf4j-jdk14. - Integration tests (
jjava-distro/src/test/java/.../*IT.java) extendContainerizedKernelCase, which mounts the locally-builtkernelspec/javaandsrc/test/resourcesinto aneclipse-temurin:<jdkVersion>container and runsjupyteragainst the real kernel. These tests need a working Docker daemon and the kernel jars must already be packaged (Failsafe runs in theverifyphase, afterpackage).