Releases: Jahia/jcr-stats
2_1_3
JCR Stats 2.1.3
Release date: 2026-06-25 · Requires Jahia 8.2.1.0+
A small admin-UI patch release. The JCR Stats admin page now scrolls vertically
when its content is taller than the screen, and the Saved executions history
is shown above the results. Drop-in upgrade from 2.1.2 — no API, permission, or
data changes.
🐛 Fixed
- The admin page now scrolls when content exceeds the screen. Jahia's admin
(moonstone) settings frame is a fixed-height region, so a route must provide its
own scrolling. The JCR Stats page didn't, so once the results, flamegraph and the
Saved-executions / Exclusions panels stacked taller than the frame, the bottom was
clipped with no scrollbar. The page now owns a vertical scrollbar, so nothing is
ever cut off.
🔧 Changed
- "Saved executions" moved above the results. The saved-run history now appears
directly under the controls — above the View (flamegraph / tree / largest /
diff) section — so past runs are the first thing you reach. The panel still
disappears entirely when there is no history, and the Exclusions panel stays
below the results.
✅ Quality
- Backend: 88 Java unit tests. Frontend: Jest suite green, ESLint clean.
- End-to-end: 33/33 Cypress tests passing (admin UI, API, permissions). The
flamegraph viewport test was updated to the new layout — it now asserts the
flamegraph stays full-width with a usable height inside the scrollable container
(the flamegraph may extend below the fold on a short window, which is expected now
that the page scrolls).
Upgrade notes
- Drop-in from 2.1.2 — no migration. Admin route unchanged
(/jahia/administration/jcrStats), still gated by thejcrStatsAdminpermission.
2_1_2
JCR Stats 2.1.2
Release date: 2026-06-25
A usability and robustness release for whole-site analysis: computations no
longer abort on un-representable nodes, long runs can be cancelled, branches can
be excluded, and every run is saved so it can be reopened and compared later.
Also a broad hardening pass across security, accessibility (WCAG 2.2 AAA), and
reliability. Drop-in upgrade from 2.1.x.
Highlights
- 🌳 Whole-site computations no longer crash on nodes with names that aren't
valid JCR paths (e.g. external mounts). - ⏹️ Cancel a running computation for real — it stops on the server.
- 🚫 Exclude paths from a computation, persisted in an OSGi config file.
- 🗂️ Saved executions — every run is auto-saved as JSON; reopen, compare, or
delete past runs. - ♿ WCAG 2.2 Level AAA accessibility pass on the admin UI.
Added
Saved executions (server-side JSON history)
Every completed computation is automatically saved as a JSON snapshot in the JCR
(/sites/systemsite/files/jcr-stats/snapshots/), so a past run's flamegraph can
be reopened later — by anyone with the jcrStatsAdmin permission, and across
restarts. A Saved executions panel lists the history (most recent first) with
its date and size, and per-row View (reload into the viewer), Compare
(diff against the current result), and Delete actions. Snapshots are capped
at the 50 most recent (oldest pruned on write). New GraphQL:
jcrStats.snapshots, jcrStats.saveSnapshot, jcrStats.deleteSnapshot.
Path exclusions
Exclude a path — and its whole subtree — from the computation. Click a frame in
the flamegraph and choose Exclude this path; excluded paths are listed with a
Remove control. Exclusions are persisted as an OSGi configuration file
(${karaf.etc}/org.jahia.community.jcrstats.cfg, property
jcrStats.excludedPaths) via a ManagedService, so they survive restarts and
can be hand-edited. New GraphQL: jcrStats.exclusions,
jcrStats.addExclusion(path), jcrStats.removeExclusion(path). Exclusions take
effect on the next computation.
Server-side cancellation
A new jcrStats.cancel mutation and a cancelled flag on jcrStats.status. The
traversal polls a cooperative cancellation flag at the start of every node, so a
running job stops between nodes (never mid-JCR-operation). The Cancel button
now actually stops the server-side job.
Changed
- "Load data" joins the history — loading a JSON file now also stores it as a
saved execution (so loaded data is persisted alongside computed runs), and the
standalone Compare with… button was removed; comparison is driven entirely
from the Saved executions list. - Saved-executions UX — rows show date and size, with per-row Delete and a
capped, scrollable list; Compare is gated until a current result is loaded.
Fixed
- Whole-site traversal no longer aborts on un-listable branches. Descending
into an external data-source mount whose child name isn't a valid JCR path
(e.g. acloud-dumpsnode named with an ISO-8601 timestamp, where:is the
namespace-prefix separator) previously raised
RepositoryException: Invalid path … ':' not valid name characterand aborted
the whole computation. Direct listing now falls back to an escaped
ISCHILDNODEquery, recovering the valid children; only the single
un-representable node is omitted. TheMAX_VISITED_NODESsafety cap is
unchanged. - The "Cancel" button now stops the computation instead of only stopping the
client-side polling. - Snapshot list reflects deletes immediately — listing now iterates the
folder's children directly instead of an index-backed query that lagged behind
deletions. - Review hardening: snapshot/flamegraph writes use a dedicated system session;
cancellation is classified by exception type; the flamegraph write aborts on a
failed header/footer;jsonEscapehandles lone surrogates;jcr:datareads
guard multi-valued properties; assorted frontend race fixes (no stale-run result
applied to a new run; load-as-snapshot failures surfaced).
Accessibility (WCAG 2.2 Level AAA)
Focus is no longer relocated on async completion (only on user-initiated view
changes); reduced-motion fully stops the progress-bar animation; info/success use
role="status" and errors role="alert"; list/icon buttons carry descriptive
accessible names; the flamegraph uses role="img" with the keyboard-accessible
tree-table as its alternative; the progressbar exposes aria-valuemin/max;
delete uses an accessible inline two-step confirm; borders/links meet AAA
contrast; headings reflow at 400% zoom.
Security
saveSnapshotvalidates the uploaded envelope structurally (parsed JSON
withformat = jcr-stats-flamegraphand atree), not by substring match.deleteSnapshotonly deletes within the snapshots folder.- Excluded-path validation hardened (absolute, length-capped, segment-scoped
./..rejection, no ReDoS-prone regex). - All GraphQL operations remain gated by the
jcrStatsAdminpermission; JCR
traversal uses a privileged system session by design.
Quality
- Backend: 88 unit tests. Frontend: 108 Jest tests. End-to-end: 33 Cypress tests
(admin UI, API, permissions) — all green. SonarQube quality gate passing;
ESLint clean.
Upgrade notes
- Drop-in from 2.1.x — no API or data migration required. The admin route is
jcrStats(/jahia/administration/jcrStats). - Assign the
jcr-stats-administratorrole (or thejcrStatsAdminpermission)
to users who should access the tool. - Saved snapshots and generated flamegraph reports accumulate in the JCR under
/sites/systemsite/files/jcr-stats/(snapshots are capped at 50; HTML reports
are not — prune if needed).
Known limitations
- The computation is a single, server-wide job — concurrent admins share one run.
- The subtree is held in memory, bounded by the
MAX_VISITED_NODES = 5,000,000
safety cap. - The read-only
size/nodeCount/treeGraphQL queries run synchronously on the
request thread (use the asynccompute/status/resultflow for large paths).
Compatibility
- Requires Jahia 8.2.1.0 or later.
2_1_1
JCR Stats 2.1.1
Release date: 2026-06-23
A performance and maintenance release. JCR Stats now traverses the repository
significantly faster on large subtrees, with no change to the analysis output or
the admin UI. Drop-in upgrade from 2.1.0.
Highlights
- ⚡ Faster tree traversal — large-subtree computations no longer pay a
per-node query cost, so deep/wide analyses finish noticeably quicker. - 🎯 More accurate — size and node counts now read committed repository state
instead of possibly-lagging index state.
Improvements
Direct hierarchy traversal
The size computation now walks the JCR hierarchy directly via
JCRNodeWrapper.getNodes() instead of issuing one ISCHILDNODE JCR-SQL2 query per
node. This removes, at every node:
- a query parse / plan / Lucene-index lookup, and
- a redundant path re-resolution of each child.
The bigger and deeper the subtree, the larger the speed-up. Results are also more
accurate because traversal reads committed hierarchy state rather than the
query index, which can lag behind recent writes.
via a system property (see Configuration below). The default is the new direct
strategy; no action is required to get the improvement.
Reduced session overhead
session.refresh(false) now runs once at the start of a traversal instead of once
per node — eliminating needless per-node refresh work on a read-only walk.
Configuration
| System property | Values | Default | Effect |
|---|---|---|---|
jcrStats.traversal |
direct, query |
direct |
direct uses getNodes() hierarchy iteration; query restores the legacy per-node ISCHILDNODE JCR-SQL2 strategy. |
To A/B compare on the same build, set -DjcrStats.traversal=query in your
JAVA_OPTS / setenv, restart, run the same path, and compare the
INFO log line:
JCR stats computation finished for path in ms ( nodes visited)
Tests
- Added
JcrStatsTraversalTestcovering the direct-traversal aggregation logic
(size roll-up, node count, single-visit guarantee, size-descending child
ordering) with mockedJCRNodeWrappers.
Upgrade notes
- Drop-in upgrade from 2.1.0 — no API, permission, configuration, or data
changes. Public GraphQL operations, the Karaf command, and the admin UI are
unchanged. - The behavior change is internal (how children are fetched during traversal); the
computed sizes and node counts are equivalent.
Compatibility
- Requires Jahia 8.2.1.0 or later (unchanged from 2.1.0).
2_1_0
JCR Stats 2.1.0
Release date: 2026-06-23
JCR Stats turns a raw JCR subtree into something you can actually read: an
interactive flamegraph, a sortable tree-table, and a top-largest list — weighted
by storage size or by number of nodes. This release adds a full GraphQL API, an
asynchronous computation model for very large trees, and a redesigned admin UI.
Highlights
- 📊 Interactive admin UI — flamegraph, tree-table, top-largest, and snapshot
comparison views, available under Administration → System Health → JCR Statistics. - ⚡ Asynchronous computation — large subtrees are traversed in the background
with live progress, so requests no longer time out. - 🔌 GraphQL API — script and automate the same analysis the UI uses.
- 🔐 Dedicated permission & role — access is gated by a new
jcrStatsAdmin
permission.
New features
GraphQL API
A new jcrStats namespace on the root Query and Mutation:
- Queries:
size(path),nodeCount(path),tree(path, maxDepth),
status(),result(maxDepth),reports() - Mutations:
compute(path)— fire-and-forget asynchronous startcomputeSize(path, deleteTemporaryFile)— synchronous full flamegraph
- The node tree is returned recursively with depth limiting to keep payloads small.
- Every operation requires the
jcrStatsAdminpermission.
Asynchronous computation
A single server-wide background job model for large subtree traversal:
compute(path)starts one job (no-op if one is already running).status()reports live progress: running flag, elapsed time (ms), visited-node
count, path, and error state.result(maxDepth)returns the cached tree once computation finishes.- The admin UI polls status every 2s and shows an elapsed timer + live node counter.
- Resume-on-remount: leaving the page and coming back re-attaches to a job
still in progress.
Admin UI
An interactive space-analysis interface at /jahia/administration/jcrStats:
- Flamegraph view — click-to-zoom visualization of space usage.
- Tree-table view — hierarchical breakdown with size, % of total, % of parent,
and node count; fully keyboard accessible. - Largest-items view — sorted top-N space consumers.
- Comparison view — diff two snapshots to see what changed over time.
- Snapshot save/load — download the current tree as JSON and reload baselines.
- jContent deep links — jump straight from a result into the jContent editor
for the selected node.
Metrics
Weight any analysis by size (bytes) for disk-usage analysis or by number of
nodes for structure/complexity analysis.
Shared computation engine
A reusable JcrStatsComputer now powers all three entry points — the
jcr-stats:compute-size Karaf command, the GraphQL API, and the admin UI — so they
always produce consistent results.
Improvements
- Single admin menu entry — the previous two-level JCR Statistics → Compute size
navigation is collapsed into one clickable JCR Statistics entry under
System Health. - Job logging — the asynchronous computation now logs an
INFOline when a job
starts and when it finishes (with elapsed time and visited-node count), making
long-running scans easy to follow in the server log. - Karaf command refactored to delegate to the shared
JcrStatsComputer.
Fixes
- Defensive node-count traversal cap prevents unbounded memory use on pathologically
large trees. - Hardened JSON-import validation for snapshot upload (strict schema enforcement).
Security & access control
- New
jcrStatsAdminpermission andjcr-stats-administratorrole
(bundlingadministrationAccess+jcrStatsAdmin, marked privileged). All GraphQL
operations and the admin UI requirejcrStatsAdmin. ⚠️ Privileged traversal:compute(path)andcomputeSize(path)run under a
JCR system session, which bypasses node-level read ACLs. Holders of
jcrStatsAdmintherefore get full-repository structural/size visibility regardless
of per-node read permissions. This is intentional — administrators need the full
storage footprint — so treat thejcr-stats-administratorrole as granting broad
read-visibility into the entire JCR tree.- Generated flamegraph HTML is safe, non-executable content; path parameters are
validated and traversal is bounded by a configurable depth limit.
Deprecations
- Direct use of
ComputeSizeCommandfor programmatic access is deprecated — use the
GraphQL API orJcrStatsComputerinstead.
Upgrade notes
- The module's root package was renamed from
org.jahia.modules.*to
org.jahia.community.jcrstats, and the module is now declared as asystem
type module. - Assign the
jcr-stats-administratorrole (or grant thejcrStatsAdmin
permission) to users who should access the tool — see the security note above.
Compatibility
- Requires Jahia 8.2.1.0 or later (
jahia-modulesparent 8.2.1.0).
2_0_0
Full Changelog: 1_0_2...2_0_0
1_0_2
Correction of a bug where the content was not correctly written
1_0_1 [maven-release-plugin] copy for tag 1_0_1
First release :)
1_0_0 [maven-release-plugin] copy for tag 1_0_0