Skip to content

feat(ui): pluggable UI versioning via servlet filter (cross-platform)#6

Open
cnewkirk wants to merge 2 commits into
developfrom
feat/ui-cross-platform-versioning
Open

feat(ui): pluggable UI versioning via servlet filter (cross-platform)#6
cnewkirk wants to merge 2 commits into
developfrom
feat/ui-cross-platform-versioning

Conversation

@cnewkirk

@cnewkirk cnewkirk commented Apr 29, 2026

Copy link
Copy Markdown
Owner

Summary

Replaces the single /ui/ filesystem directory with a versioned-slot model. The Vue SPA lives in ui-versions/<name>/, and SpaRoutingFilter forwards every /ui/* request to /ui-versions/<active>/* based on a system property. Pure Java; no symlinks anywhere — works identically on Linux and Windows.

What changes

  • Maven assembly (opennms-webapp/pom.xml, root pom.xml): the SPA artifact unpacks into ui-versions/default/ instead of ui/. A new opennms.ui.version property (default ${project.version}) lets a build target a different SPA artifact version without rebuilding the backend.
  • SpaRoutingFilter: reads org.opennms.web.ui.version (default default), validates it against [A-Za-z0-9._-]+ minus .., and forwards the request via RequestDispatcher.forward(). SPA deep links fall back to <version>/index.html for HTML5 history mode; assets keep their relative path.
  • A bogus or unsafe version name fails soft to default.

Why a filter instead of a symlink

The earlier sketch used ln -sfn ui ui-versions/default at build time. That works on Linux but breaks on Windows: WAR unpacking either errors on symlink creation or leaves a regular file containing the link path. A servlet filter is portable, has no filesystem-API surface, and lets the version be re-read per request (so future runtime swap mechanisms — Karaf shell, JMX, properties reload — slot in without changes here).

Live PoC

Property /ui/ index /ui/assets/index.js /ui/assets/index-<hash>.js
unset (→ default) unhashed index.js 200 (in default/) 404
next index-<hash>.js 404 200 (in next/)

Same URL, two bundles, no symlinks. Verified end-to-end in a 35.0.4 podman container with the patched opennms-web-api jar dropped in.

Test plan

  • mvn test -pl opennms-web-api -Dtest=SpaRoutingFilterTest — 8/8 passing (default + named, asset paths, SVG, dotted versions like 35.1.0-beta, .. rejection, slash rejection, non-/ui passthrough)
  • Builds clean against develop on JDK 21
  • Manual: install two SPA bundles into ui-versions/default/ and ui-versions/next/, toggle property, hit /opennms/ui/ — content swaps. No restart required at the routing layer (the property is re-read per request; what needs a restart today is loading the property from etc/opennms.properties.d/)

Relationship to other PRs in this series

Toward a user-friendly opt-in/opt-out

The runtime mechanism is now hot-swap-friendly (filter re-reads the property per request), but the property is loaded at JVM start. A future PR can wire system:property -p from Karaf shell — or a small JAX-RS endpoint behind admin auth — so an admin can flip versions without a restart. Out of scope here; this PR locks down the routing layer first.

Chance Newkirk added 2 commits April 28, 2026 07:40
The webapp assembly now unpacks the SPA artifact to
target/.../ui-versions/default/ instead of target/.../ui/. A new
opennms.ui.version Maven property (default ${project.version}) lets the
build target a different SPA artifact version without rebuilding the
backend.

The /ui/ filesystem path is no longer materialized at build time. The
SpaRoutingFilter (separate commit) forwards /ui/* requests to
/ui-versions/<version>/* at request time, which works on Windows as
well as Linux — no symlinks anywhere.
SpaRoutingFilter now reads org.opennms.web.ui.version (default "default")
and forwards /ui/* requests to /ui-versions/<version>/* via Jetty's
RequestDispatcher.forward() — pure Java, no symlinks. Deep links fall
back to <version>/index.html for SPA history mode; assets keep their
relative path. Version names matching [A-Za-z0-9._-]+ minus ".." are
accepted; anything else falls back to "default".

Replaces the symlink-based ui -> ui-versions/default mechanism so the
mechanism works on Windows as well as Linux.

Includes 8 unit tests covering default + named versions, asset/SPA/SVG
paths, traversal rejection, and non-/ui passthrough.
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.

1 participant