Repository navigation
[WP8] Native-image / AOT: user-POJO hints uncovered + nativeTest never run in CI #185
Description
此內容由 AI 產生(specification-mapper 全庫審查 2026-07-16)。
Work package WP8 · findings: COR-04, TEST-02 · worst severity: medium
From the 2026-07-16 whole-codebase review (branch jakarta, modules mapper/ + starter/). Each finding below is CONFIRMED by independent adversarial verification. File paths and line numbers reflect the review snapshot and may drift — treat them as starting points, not exact coordinates.
COR-04 — Native-image reflection hints cover only jars shipping their own Jandex index; user query POJOs are silently unfiltered, and the contract is undocumented
- Category / severity: correctness / medium · effort M · confidence medium
- Location:
mapper/src/main/java/tw/com/softleader/data/jpa/spec/aot/SpecMapperRuntimeHints.java:50 - Evidence: SpecMapperRuntimeHints registers hints only for classes found in classpath*:/META-INF/spec-mapper/reflect-config.idx; the only such index is produced for the mapper's own classes by jandex-maven-plugin (mapper/pom.xml:59-76). The starter ships no aot.factories or hints of its own (starter/src has none), and no documentation anywhere in the repo (site/content and README greps for jandex/reflect-config/native-image are empty) tells users to produce an index for their query POJOs or custom SimpleSpecification subclasses.
- Failure scenario: A GraalVM native-image app uses the starter with a CustomerQuery POJO carrying @SPEC fields; the POJO gets no reflection metadata, so ReflectionDatabind's getDeclaredFields()/makeAccessible either sees zero fields (spec becomes null and findBySpec returns every row — filters silently dropped) or throws MissingReflectionRegistrationError, depending on GraalVM reachability-metadata mode; custom @SPEC(value=MySpec.class) classes likewise fail in SimpleSpecification.newSpec.
- Suggested fix: Document the META-INF/spec-mapper/reflect-config.idx contract (jandex-maven-plugin snippet) for native-image users, and/or add an AOT BeanFactoryInitializationAotProcessor in the starter that registers hints for types reachable from repository QueryBySpecExecutor signatures.
- Verification: [CONFIRMED] Traced the full mechanism and every load-bearing claim holds.
-
SpecMapperRuntimeHints.java:49-58 registers reflection hints ONLY from resources matching
classpath*:/META-INF/spec-mapper/reflect-config.idx, reading each as a Jandex index and registering every known class with all MemberCategory values. It never scans user application classes. -
mapper/pom.xml:59-73 shows the only producer of that index is the mapper module's own jandex-maven-plugin (indexDir target/classes/META-INF/spec-mapper, indexName reflect-config.idx). No other module/jar produces this path, so
classpath*:matches only the mapper's own classes. Confirmed the starter ships no hints: only spring/aot.f…
TEST-02 — nativeTest profile is never exercised by any CI pipeline — GraalVM/AOT support can regress silently
- Category / severity: testing / medium · effort M · confidence high
- Location:
Jenkinsfile:107 - Evidence: The Makefile defines
test-native(mvn -PnativeTest process-classes test) and the parent pom carries a full nativeTest profile (process-test-aot + native-maven-plugin test execution), but the Jenkinsfile's stages run onlymake compileandmake test(Unit Tests stage, and Matrix Tests alsomake test), and the six GitHub workflows (bump-snapshot, bump-spring, commitlint, label-issues, release-please, site-build) contain no maven test invocation at all. The JVM-mode hint tests (SpecRuntimeHintsTest, DomainRuntimeHintsTest, AnnotationRuntimeHintsTest) check hint registration but never run code in an actual native image. - Failure scenario: A new SimpleSpecification subclass or annotation is added without landing in the Jandex index consumed by SpecMapperRuntimeHints (or a GraalVM/Spring AOT upgrade breaks processing); every JVM test stays green, the release ships, and native-image users hit reflection/instantiation failures at runtime.
- Suggested fix: Add a CI stage (Jenkins or a GitHub Actions job with graalvm/setup-graalvm) running
make test-nativeat least on the release Java/Spring combination. - Verification: [CONFIRMED] Traced every claim. Jenkinsfile lines 108 and 136 invoke only
make test(which ismvn process-classes test) and the Compile stage runsmake compile; no stage runsmake test-nativeor-PnativeTest. Makefile confirmstest-native: mvn -PnativeTest process-classes test, unused by CI. pom.xml:318-362 defines a fullnativeTestprofile (spring-bootprocess-test-aot+ graalvm native-maven-pluginnative-testgoal). Grep across .github/workflows/ found no maven test invocation. The three RuntimeHints tests exist and are ordinary JVM tests that assert hint registration, never executing in a native image. So the nativeTest/AOT path is genuinely never exercised by any pipeli…
Full report: see the codebase-review-2026-07-16 branch (linked on the parent tracking issue).
Activity
shihyuho commentedon Jul 16, 2026
Triage Notes - needs human
Category: bug (primary sub-finding COR-04 is a native-image correctness defect; TEST-02 is a CI/test-coverage gap)
Summary: In a GraalVM native image the library registers reflection hints only for its own classes, so user query POJOs and custom specification subclasses get no metadata and either silently drop all filters or fail at runtime — and no CI stage exercises the native/AOT path, so this can regress undetected.
Current behavior:
SpecMapperRuntimeHints(registered as aRuntimeHintsRegistrarvia the mapper module'sMETA-INF/spring/aot.factories) registers reflection hints exclusively from Jandex indexes found atclasspath*:/META-INF/spec-mapper/reflect-config.idx. The only producer of that resource is the mapper module's ownjandex-maven-pluginexecution, soclasspath*:matches only the library's own classes.aot.factoriesand noRuntimeHintsRegistrarof its own (only itsAutoConfiguration.imports).ReflectionDatabindwalks the user query POJO's fields viadoWithLocalFields(target.getClass(), ...)and reads them withReflectionUtils.makeAccessible+getField;SimpleSpecification.newSpecinstantiates customSimpleSpecificationsubclasses reflectively viaaccessibleConstructor(...).newInstance(...). In a native image without hints for those user types,getDeclaredFieldsreturns nothing (spec built from zero fields → filters silently dropped → every row returned) or aMissingReflectionRegistrationErroris thrown, and custom@Spec(value=...)classes fail to instantiate.site/content, CONTRIBUTING) describing theMETA-INF/spec-mapper/reflect-config.idxcontract or telling native-image users how to make their query POJOs / custom specs reachable.Jenkinsfileruns onlymake compile,make test, and matrixmake test; the six GitHub workflows invoke no Maven test at all. Thetest-nativeMakefile target (mvn -PnativeTest process-classes test) and the parent pom'snativeTestprofile (process-test-aot+native-maven-plugin) exist but are never invoked by any pipeline. The three JVM hint tests (SpecRuntimeHintsTest,DomainRuntimeHintsTest,AnnotationRuntimeHintsTest) only assert hint registration on the JVM — they never build or run an actual native image.Desired behavior:
@Spec-annotated query POJOs and customSimpleSpecificationsubclasses — either automatically, or via a clearly documented, supported mechanism the user applies.Key interfaces (named types/methods):
SpecMapperRuntimeHints(implementsorg.springframework.aot.hint.RuntimeHintsRegistrar) — theregisterHints(RuntimeHints, ClassLoader)method and theREFLECT_CONFIG_LOCATIONconstant.ReflectionDatabind— theof(...)factory that callsdoWithLocalFields(target.getClass(), ...)andgetFieldValue(...)usingReflectionUtils.makeAccessible.SimpleSpecification#newSpec(Context, Class<? extends SimpleSpecification>, String, Object)— reflectiveaccessibleConstructor(...).newInstance(...).QueryBySpecExecutor(starter) — candidate anchor for an AOTBeanFactoryInitializationAotProcessorif the auto-registration route is chosen.nativeTestprofile,Makefiletest-nativetarget,Jenkinsfilestages,.github/workflows/.Acceptance criteria (depend on the chosen approach — see Needs human):
META-INF/spec-mapper/reflect-config.idxmechanism (including ajandex-maven-pluginsnippet) for native-image users.MemberCategoryvalues) for the relevant user types, verified by an AOT/hints unit test in the starter module.make test-native/-PnativeTest) on at least the release Java/Spring combination, wired into whichever pipeline the maintainers designate.Out of scope:
classpath*:/META-INF/spec-mapper/reflect-config.idxsemantics for the library's own classes.Needs human because:
reflect-config.idxcontract and require users to runjandex-maven-pluginon their own modules; (b) ship an automatic AOTBeanFactoryInitializationAotProcessorin the starter; or (c) both. Option (b) is not mechanically obvious: the user query POJO type is passed as a runtime argument toSpecMapper/QueryBySpecExecutor, not declared in any repository generic signature, so "types reachable from repositoryQueryBySpecExecutorsignatures" does not actually capture the POJOs that need hints — deciding what to scan (component-scan for@Specfields? entity graph? user opt-in annotation?), the default on/off, and the resulting public contract is a maintainer policy call, not a self-contained coding task.make test-nativeneeds a GraalVM native-image toolchain (e.g.graalvm/setup-graalvm) and a new Jenkins/GitHub Actions stage; this is release/CI infrastructure that cannot be self-verified with plainmvn testin an isolated worktree, and which pipeline owns it (and its cost on the build matrix) is a maintainer decision. The concrete CI acceptance criteria also cannot be finalized until the COR-04 approach is chosen, since that determines what the native test must assert.shihyuho commentedon Jul 20, 2026
Superseded by #202 in the execution map #204, which carries the same findings (COR-04, TEST-02). This 2026-07-16 static plan is being retired in favour of that map.