Skip to content

fix: compose JAX-RS method-level @Path with class-level prefix#1007

Open
CharlesQueiroz wants to merge 1 commit into
DeusData:mainfrom
CharlesQueiroz:fix/jaxrs-path-composition
Open

fix: compose JAX-RS method-level @Path with class-level prefix#1007
CharlesQueiroz wants to merge 1 commit into
DeusData:mainfrom
CharlesQueiroz:fix/jaxrs-path-composition

Conversation

@CharlesQueiroz

Copy link
Copy Markdown

Fixes #1005.

Root cause

extract_route_from_annotations() returned on the first mapping annotation. For JAX-RS methods the verb and the path live in two sibling annotations:

@GET
@Path("/count")
public CountResponse count() { ... }

The @GET matched first (annotation_route_method("GET")), defaulted the path to "/", and the sibling @Path was never read: every method-level @Path was silently dropped. Class-level @Path prefixes were never recognized either, because a lone @Path carries no verb, so the scan reported "no route" and spring_class_route_prefix() returned NULL.

This also explains the "inconsistent" symptom in #1005: resources whose endpoints are exercised by RestAssured tests got Route nodes from the tests' full-URL string literals, masking the missing annotation-derived routes; resources without such tests showed only the class-level route.

Fix

New scan_route_annotations() collects the whole annotation set in one pass: the mapping verb (+ optional inline path, Spring style) and a separate JAX-RS @Path value. Then:

  • method-level (extract_route_from_annotations): a verb annotation is still required (a lone @Path sub-resource locator is not an endpoint); path = inline mapping path, else @Path, else "/".
  • class-level (spring_class_route_prefix): inline mapping path (@RequestMapping("/api")), else class-level @Path.
  • Bare @HEAD/@OPTIONS verbs are now recognized alongside GET/POST/PUT/DELETE/PATCH.

Spring extraction is unchanged: mapping annotations keep their inline path and the collecting scan preserves the first-mapping-annotation-wins order.

Validation

  • New regression test extract_java_jaxrs_path_composition_issue1005 (class prefix alone, prefix + method @Path, verb preserved).
  • Full suite: 5986 passed, 1 skipped, 0 failures.
  • End-to-end fixture (sanitized copy of the failing resource shape from Java/JAX-RS: method-level @Path sub-routes inconsistently composed with class-level prefix #1005, constructor @ConfigProperty params and generic DTO return types included) indexed with the patched binary now yields:
    • __route__GET__/api/v1/widgets
    • __route__GET__/api/v1/widgets/count
    • __route__POST__/api/v1/widgets/{}/archive
    • Spring control __route__GET__/api/v2/things/all unchanged.

@CharlesQueiroz CharlesQueiroz requested a review from DeusData as a code owner July 10, 2026 16:04
@DeusData DeusData added bug Something isn't working parsing/quality Graph extraction bugs, false positives, missing edges priority/normal Standard review queue; useful PR with ordinary maintainer urgency. labels Jul 10, 2026
@DeusData DeusData added this to the 0.9.1-rc milestone Jul 10, 2026
@DeusData

Copy link
Copy Markdown
Owner

Thanks for the targeted JAX-RS fix. I have triaged it into 0.9.1-rc as the fix candidate for #1005. Review focus will be class-plus-method path composition and making sure Spring-style annotation extraction does not regress.

@DeusData

Copy link
Copy Markdown
Owner

Reviewed and verified locally — the diagnosis and fix are exactly right; same two mechanical blockers as your #1008, plus one recommendation.

Verified (your branch merged onto current main, db330bf):

  • your unit test passes; extraction suite 214/214
  • end-to-end on a real index: the JAX-RS resource now yields /api/v1/widgets and /api/v1/widgets/count (previously both methods collapsed onto a single __route__GET__/ node — independently reproduced on main before applying your branch)
  • Spring non-regression confirmed end-to-end: @RestController + class @RequestMapping("/api/orders") + @GetMapping("/list") still composes to /api/orders/list

1. DCO (blocking): commit 07704e1 lacks a Signed-off-by matching its author — git commit --amend -s + force-push.

2. clang-format (blocking): two lines in extract_defs.c (~1299–1300, the HEAD/OPTIONS condition) need wrapping — make lint-format locally shows the exact form.

3. Recommendation (would take this from good to complete): add an end-to-end Route-node test alongside the unit test — tests/test_edge_types_probe.c has et_routes_exact, which asserts the exact Route-node name set through the full pipeline. That's the tier that matches the reporter's actual symptom (collapsed/missing Route nodes and the dedup onto __route__GET__/), and it guards the class-prefix composition + emission dedup path your unit test can't see. Shape:

static const char *routes[] = {"/api/v1/widgets", "/api/v1/widgets/count", NULL};
ASSERT_TRUE(et_routes_exact(f, 1, routes));

If you'd rather keep this PR minimal, say so and we'll add the probe-tier guard in a follow-up after merge.

Known gap we're both fine deferring: @ApplicationPath base-path composition (cross-class, like Spring's cross-file case) — worth its own issue once this lands.

With DCO + format in and CI green, this is good to merge from my side. Two solid, well-scoped fixes in a row — much appreciated.

…ata#1005)

JAX-RS splits a route across two annotations: the verb comes from a bare
@GET/@POST/... and the path from a sibling @path. The annotation scan
returned on the first mapping annotation, so the @get matched, defaulted
the path to "/" and the sibling @path was never read. Class-level @path
was never recognized as a prefix either, because a lone @path carries no
verb and the scan reported no route.

scan_route_annotations() now collects the whole annotation set (mapping
verb + optional inline path, and a separate JAX-RS @path), then:
- method-level: verb required; path = inline mapping path, else @path,
  else "/"
- class-level prefix: inline mapping path, else @path
Also recognizes bare @HEAD/@options verbs.

Spring behavior is unchanged (mapping annotations keep their inline
path); covered by the existing test suite plus a new regression test.

Signed-off-by: Charles Queiroz <fcqueiroz@liquibase.com>
@CharlesQueiroz CharlesQueiroz force-pushed the fix/jaxrs-path-composition branch from 07704e1 to 041eb99 Compare July 14, 2026 08:34
@CharlesQueiroz

Copy link
Copy Markdown
Author

Applied the two blockers: sign-off added and the HEAD/OPTIONS condition in extract_defs.c is clang-format clean. Also took the recommendation rather than deferring it: added a handles_jaxrs_java probe to test_edge_types_probe.c asserting the exact Route-name set {/api/v1/widgets, /api/v1/widgets/count} through the full pipeline, guarding the class-prefix composition and the emission dedup path. Full suite passes locally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working parsing/quality Graph extraction bugs, false positives, missing edges priority/normal Standard review queue; useful PR with ordinary maintainer urgency.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Java/JAX-RS: method-level @Path sub-routes inconsistently composed with class-level prefix

2 participants