Skip to content

One icon controller, for whichever library is on the classpath - #110

Merged
ctgnz merged 1 commit into
masterfrom
feat/93-library-agnostic-server
Sep 26, 2026
Merged

ctgnz merged 1 commit into
masterfrom
feat/93-library-agnostic-server

Conversation

@ctgnz

@ctgnz ctgnz commented Sep 26, 2026

Copy link
Copy Markdown
Owner

Closes #93. Prerequisite for #102.

jmsfx-creator has run against any symbology library since #76. jmsfx-server did not: it compiled against jmsfx-standard, and its icon API was 23 controllers, one per symbol set, each binding IconRestController's six type parameters to one set's generated enums.

Those subclasses existed for one reason. Spring converts a @PathVariable of an enum type through Enum.valueOf, so a handler had to name a concrete enum to accept one — which made the class per-set and the module per-library. #90 is what that cost: several of those bindings were wrong and only a request could reveal it.

29 files changed, 582 deletions against 231 insertions.

The shape

The symbol set moves into the path and a HandlerMethodArgumentResolver does the resolving. A resolver can do what a Converter cannot — see the rest of the request — so every value is resolved within the symbol set named earlier in the same path, and the handlers take jmsfx-core's interfaces. No generated type is named anywhere in the module now.

The URLs are unchanged. Each subclass's prefix was SymbolSetSummary.pathFor applied to its set, so it is now matched rather than hard-coded, and path segments are still the enum constant names CodeElementSummary publishes. WebController and IconGeneratorController resolve through IconLibrary.discover() instead of SymbolSetEnum.values().

Two things this turned up

  • The server never declared what it compiled against. jmsfx-core arrived transitively through jmsfx-standard, so demoting that dependency broke the compile until jmsfx-core was declared — which it should have been all along.
  • jmsfx-standard is runtime but not optional, unlike jmsfx-creator's, and contrary to what Make jmsfx-server library-agnostic, with one concrete IconRestController #93 proposed. spring-boot:repackage leaves optional dependencies out of BOOT-INF/lib, so the fat jar started and then failed every request with "No IconLibrary on the classpath". optional is the wrong tool regardless: it stops a dependency reaching consumers, and nothing consumes the server as a library. runtime alone is what prevents anything compiling against it, and it keeps the jar runnable with a default library — swapping that one jar is then the whole of "an instance per library".

One behavioural change, deliberate

An unknown entity, modifier or amplifier now answers 404 where it used to answer 400. Enum conversion failing was a type mismatch, hence 400; a value that does not exist in the library is a missing resource, and an unknown symbol set already answered 404. Everything unknown now answers the same way. Easy to revert if you would rather keep 400.

Verification

The strongest check available: the live 1.5.0 site still runs the old 23 controllers, so the same URLs can be fetched from both.

  • 186 URLs compared byte-for-byte against production — every symbol set, every endpoint family, including the two-segment /{entityType}/{entitySubType} paths and ?trim=true. All identical.
  • A 648-request sweep of the reworked server across all 23 sets — all 200.
  • The five pages and the Browse fragment render the same icon URLs as before.
  • mvn verify green.

🤖 Generated with Claude Code

jmsfx-creator has run against any symbology library since #76. jmsfx-server did
not: it compiled against jmsfx-standard, and its icon API was 23 controllers, one
per symbol set, each binding IconRestController's six type parameters to one
set's generated enums.

Those subclasses existed for one reason. Spring converts a @PathVariable of an
enum type through Enum.valueOf, so a handler had to name a concrete enum to
accept one - which made the class per-set and the module per-library. #90 is what
that cost: several bindings were wrong, and only a request could reveal it.

The symbol set moves into the path and a HandlerMethodArgumentResolver does the
resolving. A resolver can do what a Converter cannot - see the rest of the
request - so every value is resolved within the symbol set named earlier in the
same path, and the handlers take jmsfx-core's interfaces. No generated type is
named anywhere in the module now, and 23 files are gone.

The URLs are unchanged. Each subclass's prefix was SymbolSetSummary.pathFor
applied to its set, so it is matched rather than hard-coded, and path segments
are still the enum constant names CodeElementSummary publishes.

WebController and IconGeneratorController resolve through IconLibrary.discover()
rather than SymbolSetEnum.values().

Two things this turned up:

- The server never declared what it compiled against. jmsfx-core arrived
  transitively through jmsfx-standard, so demoting that dependency broke the
  compile until jmsfx-core was declared - which it should have been all along.
- jmsfx-standard is runtime scope but not optional, unlike jmsfx-creator's.
  spring-boot:repackage leaves optional dependencies out of BOOT-INF/lib, so the
  fat jar started and then failed every request with "No IconLibrary on the
  classpath". optional is the wrong tool here anyway: it stops a dependency
  reaching consumers, and nothing consumes the server as a library. runtime alone
  is what stops anything compiling against it, and it keeps the jar runnable with
  a default library - swapping that one jar is then the whole of "an instance per
  library".

One behavioural change, deliberate: an unknown entity, modifier or amplifier now
answers 404 rather than 400. Enum conversion failing was a type mismatch, hence
400; a value that does not exist in the library is a missing resource, and an
unknown symbol set already answered 404. Everything unknown now answers the same
way.

Verified against the live 1.5.0 server, which still runs the old controllers:
186 URLs covering every symbol set and every endpoint family, compared
byte-for-byte - all identical. Plus a 648-request sweep of the reworked server,
all 200, and the five pages and the Browse fragment rendering the same icon URLs
as before.

Closes #93

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ctgnz
ctgnz merged commit 9988b50 into master Sep 26, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make jmsfx-server library-agnostic, with one concrete IconRestController

1 participant