feat(ui): pluggable UI versioning via servlet filter (cross-platform)#6
Open
cnewkirk wants to merge 2 commits into
Open
feat(ui): pluggable UI versioning via servlet filter (cross-platform)#6cnewkirk wants to merge 2 commits into
cnewkirk wants to merge 2 commits into
Conversation
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.
This was referenced Apr 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the single
/ui/filesystem directory with a versioned-slot model. The Vue SPA lives inui-versions/<name>/, andSpaRoutingFilterforwards 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
opennms-webapp/pom.xml, rootpom.xml): the SPA artifact unpacks intoui-versions/default/instead ofui/. A newopennms.ui.versionproperty (default${project.version}) lets a build target a different SPA artifact version without rebuilding the backend.SpaRoutingFilter: readsorg.opennms.web.ui.version(defaultdefault), validates it against[A-Za-z0-9._-]+minus.., and forwards the request viaRequestDispatcher.forward(). SPA deep links fall back to<version>/index.htmlfor HTML5 history mode; assets keep their relative path.default.Why a filter instead of a symlink
The earlier sketch used
ln -sfn ui ui-versions/defaultat 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
/ui/index/ui/assets/index.js/ui/assets/index-<hash>.jsdefault)index.jsdefault/)nextindex-<hash>.jsnext/)Same URL, two bundles, no symlinks. Verified end-to-end in a 35.0.4 podman container with the patched
opennms-web-apijar dropped in.Test plan
mvn test -pl opennms-web-api -Dtest=SpaRoutingFilterTest— 8/8 passing (default + named, asset paths, SVG, dotted versions like35.1.0-beta,..rejection, slash rejection, non-/ui passthrough)developon JDK 21ui-versions/default/andui-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 frometc/opennms.properties.d/)Relationship to other PRs in this series
/opennms/lands users on the new SPA; this PR controls how/ui/is served once they're there. Either can land first; landing both makes the new UI both opt-in and portable.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 -pfrom 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.