Symptom
Starting on JDK 25, any downstream user of chronicle-values' runtime code generation (ValueModel.nativeClass() / heapClass()) fails at the first compile with:
com.sun.tools.javac.code.Symbol$CompletionFailure:
class file for NotNull not found
Caused by chronicle bytecode (chronicle-bytes / chronicle-core / others) carrying constant-pool references to org.jetbrains.annotations.NotNull and Nullable on inherited method signatures. javac walks the full compile model, tries to resolve the symbol, and fails when the annotation class file is not on its classpath.
JDK 21 silently tolerated the missing symbol. JDK 25 (and seemingly also recent JDK 24 builds) tightened annotation symbol resolution and now fail symbol completion outright.
Why this is hard to work around downstream
Chronicle calls CACHED_COMPILER.loadFromJava(cl, ...) with the value type's classloader. For embedding hosts where value types live in one classloader (e.g. the application's) but the JetBrains annotations jar is only visible in another (e.g. a plugin classloader), the compiler's classpath search never reaches the plugin classloader. Bundling the annotation jar into the plugin helps nothing; neither does CACHED_COMPILER.updateFileManagerForClassLoader(...) because:
- The hook is a silent no-op before the first compile per classloader (
fileManagerMap.get(cl) returns null).
- Chronicle-values uses the hook to register the value type, not the JetBrains annotation classes.
The only working downstream fix today is CompilerUtils.addClassPath(jarPath) from application startup, which mutates the global compiler classpath -- not great ownership-wise for downstream plugins.
Proposed fix
Add org.jetbrains.annotations.NotNull and Nullable to the static dependencyFileObjects preload in net.openhft.chronicle.values.MyJavaFileManager (the block that already preloads Chronicle's own annotations like Align, Array, Group, MaxUtf8Length, Range, and chronicle's internal NotNull):
// MyJavaFileManager.java -- existing block around line 49
Arrays.asList(
// ... existing entries ...
Align.class, Array.class, Group.class, MaxUtf8Length.class,
net.openhft.chronicle.values.NotNull.class, Range.class,
// ADD:
org.jetbrains.annotations.NotNull.class,
org.jetbrains.annotations.Nullable.class,
// ... rest ...
).forEach(c -> addFileObjects(dependencyFileObjects, c));
Doing this in chronicle-values eliminates the failure for every downstream user on JDK 25+ -- bundling, classpath mutation, and file-manager hooks all become unnecessary for the JetBrains-annotation case specifically.
The org.jetbrains:annotations artifact would need to move out of provided scope (or chronicle-values would need to pin and bundle it for its own runtime-compiler purposes), since the static block references its .class literals.
Repro
- Java 25 (Temurin/Zulu/etc., 25.0.x).
- Any chronicle-values consumer that compiles value types whose interface hierarchy carries @NotNull on inherited methods -- e.g. Gerrit's cache-chroniclemap plugin against value types defined in Gerrit core. Stack trace:
ValueModel.createClass -> CachedCompiler.loadFromJava -> ... -> Symbol$CompletionFailure.
Versions
- chronicle-values 2.25ea4 through 2026.2 (current latest BOM) exhibit the issue.
- chronicle-values 2.26.4+ added updateFileManagerForClassLoader, but that hook does not resolve this specific failure (registers value type, not annotations).
Related
- chronicle-map#289 (decision to mark org.jetbrains:annotations as provided) is correct for normal library consumers, but bites runtime-codegen users on JDK 25+.
Symptom
Starting on JDK 25, any downstream user of chronicle-values' runtime code generation (
ValueModel.nativeClass()/heapClass()) fails at the first compile with:Caused by chronicle bytecode (chronicle-bytes / chronicle-core / others) carrying constant-pool references to
org.jetbrains.annotations.NotNullandNullableon inherited method signatures. javac walks the full compile model, tries to resolve the symbol, and fails when the annotation class file is not on its classpath.JDK 21 silently tolerated the missing symbol. JDK 25 (and seemingly also recent JDK 24 builds) tightened annotation symbol resolution and now fail symbol completion outright.
Why this is hard to work around downstream
Chronicle calls
CACHED_COMPILER.loadFromJava(cl, ...)with the value type's classloader. For embedding hosts where value types live in one classloader (e.g. the application's) but the JetBrains annotations jar is only visible in another (e.g. a plugin classloader), the compiler's classpath search never reaches the plugin classloader. Bundling the annotation jar into the plugin helps nothing; neither doesCACHED_COMPILER.updateFileManagerForClassLoader(...)because:fileManagerMap.get(cl)returnsnull).The only working downstream fix today is
CompilerUtils.addClassPath(jarPath)from application startup, which mutates the global compiler classpath -- not great ownership-wise for downstream plugins.Proposed fix
Add
org.jetbrains.annotations.NotNullandNullableto the staticdependencyFileObjectspreload innet.openhft.chronicle.values.MyJavaFileManager(the block that already preloads Chronicle's own annotations likeAlign,Array,Group,MaxUtf8Length,Range, and chronicle's internalNotNull):Doing this in chronicle-values eliminates the failure for every downstream user on JDK 25+ -- bundling, classpath mutation, and file-manager hooks all become unnecessary for the JetBrains-annotation case specifically.
The
org.jetbrains:annotationsartifact would need to move out of provided scope (or chronicle-values would need to pin and bundle it for its own runtime-compiler purposes), since the static block references its .class literals.Repro
ValueModel.createClass -> CachedCompiler.loadFromJava -> ... -> Symbol$CompletionFailure.Versions
Related