Persistent Executions
ReportShell allows you to run and export precompiled .jasper files on-demand or through persistent executions.
This is conceptually similar to the BIRT Viewer document parameter,
where a fill operation creates a stored print document that subsequent exports can reuse.
This approach prevents the need to re-fill the report for every export operation and enables efficient paged navigation for large reports in web report viewers.
Unlike the BIRT Viewer, ReportShell manages persistent executions through a dedicated Executions REST API, similar to the structured workflow of JasperReports® Server (JRS). While the Render REST API does not persist executions itself, it can utilize a persistent execution ID to render or export directly from that specific execution’s print document.
ReportShell intentionally maintains API naming conventions close to JasperReports® Server (JRS) to ensure the workflow feels familiar to existing JRS users.
See REST API Reference for more details.
Ephemeral Executions
Section titled “Ephemeral Executions”Persistent executions can be marked as ephemeral when they are created. An ephemeral execution is one that is tied to a short-lived UI session (for example, a user browsing pages of a report in a web report viewer UI) rather than to a long-term workflow that needs the execution to remain available.
Ephemeral executions are functionally identical to regular executions while they are alive. The only difference is that the cleanup task is allowed to delete them eagerly once they are older than a configurable TTL, without waiting for an explicit delete request. See Cleanup and Retention for how this works in practice.
Use ephemeral executions for viewer-driven sessions where the client does not require persistence after the session ends. This is ideal for web interfaces where users may navigate away at any time, as it eliminates the need for explicit deletion and prevents resource accumulation.
Conversely, use persistent (non-ephemeral) executions for long-lived scenarios where the generated print document must be stored for future reuse, auditing, or multiple export operations across different sessions.
Storage
Section titled “Storage”ReportShell stores these persistent execution records in a backend storage system.
The default implementation uses a file system store, which saves persistent executions to a configurable directory (defaulting to the system temporary directory).
However, the architecture allows for alternative storage strategies. Implementations can be swapped to store persistent executions in a database.
Print Revisions and Accumulation
Section titled “Print Revisions and Accumulation”Each successful fill of an execution writes a new print document into storage, keyed by the execution and a monotonically increasing revision number. Updating an execution does not overwrite its print document in place. The previous revision stays on disk for a short window after the new one is written.
This exists to absorb a read-then-load race. A client that asked the store for an execution’s current revision a moment ago, and is now reading the corresponding print document, must be allowed to finish that read even if a concurrent updater has just produced a newer revision. If the store deleted the previous print as soon as a new one was written, in-flight readers would fail.
Old revisions are pruned eagerly after each successful save. The store keeps the latest revision plus a small tail of prior ones. Ephemeral executions keep a smaller tail because they are short-lived by design.
You should expect to see more than one print file per execution on disk during normal operation. This is the intended behavior of the retention scheme and is not a leak.
Paged vs Unpaged Persistent Executions
Section titled “Paged vs Unpaged Persistent Executions”In JasperReports, whether a report is paged vs unpaged is a fill-time parameter represented by IS_IGNORE_PAGINATION. Because a persistent execution in ReportShell has a one-to-one relationship with a print document, each execution is either paged or unpaged.
This matters when you want to export both paged and unpaged versions of the same report and parameter set. Each execution is locked to one pagination mode, so reusing a single execution for both is not possible. To avoid re-filling the report every time you switch modes, create two executions, one paged and one unpaged, and route each export to the appropriate one.
Print Metadata
Section titled “Print Metadata”Persistent executions are the source of print metadata used for navigation-oriented web report viewer UIs.
The REST API exposes this through the info endpoint documented in the Persistent Executions REST API.
Async Support
Section titled “Async Support”ReportShell does not support async persistent execution creation yet. Reports are filled synchronously, which may take a long time for large reports and cause request timeouts. If you run into these limitations and async execution support is important for your use case, let us know.
Cleanup and Retention
Section titled “Cleanup and Retention”When you delete a persistent execution on the default file-system store, ReportShell first soft-deletes the associated directory by writing a marker file into it, and then attempts an immediate hard-delete. If the hard-delete fails (for example because a file is still locked), the directory is left in its soft-deleted state and purged later by a periodic cleanup task.
The Cleanup Task
Section titled “The Cleanup Task”ReportShell registers a standard Spring scheduled task that periodically purges soft-deleted executions and eagerly deletes ephemeral executions that have outlived their TTL. It is enabled by default through the reportshell.execution.store.fs.cleanup.enabled property (default true).
On each run, the task:
- Finds ephemeral executions older than the configured TTL and soft-deletes them.
- Purges all executions that have been soft-deleted for longer than the configured grace period.
Configuration
Section titled “Configuration”Schedule:
reportshell.execution.store.fs.cleanup.fixed-delay: Fixed delay between runs. Defaults toPT1H(1 hour).reportshell.execution.store.fs.cleanup.initial-delay: Delay before the first run after startup. Defaults toPT5M(5 minutes).
Retention thresholds (checked on every run):
reportshell.execution.store.fs.cleanup.grace-period: How long a soft-deleted execution is kept before being purged. Configured as a duration string, e.g.P1D(1 day).reportshell.execution.store.fs.cleanup.ephemeral-ttl: How long an ephemeral execution can live before the cleanup task deletes it. Configured as a duration string, e.g.PT1H(1 hour).
Execution Lifecycle
Section titled “Execution Lifecycle”ReportShell exposes hooks that let your application participate in the lifecycle of an execution without subclassing services or wrapping the store. The entry point is the ExecutionLifecycleListener interface. Any Spring bean that implements it is picked up automatically, and multiple listeners run in @Order sequence.
There are four moments where a listener can run:
- creating: before the report is filled and before the execution record is written. Useful for stamping audit identity such as the creating user, validating or mutating parameter values, or attaching application context that should travel with the execution.
- updating: before the report is re-filled and the existing record is updated. The listener sees the previously attached application context and can read or replace it.
- printLoading: before the print document is read back from storage for export or navigation. This is where load-time customization belongs, including virtualizer selection.
- deleting: before the execution is removed through a service call such as a REST API delete. Useful for capturing who deleted the execution and why for audit purposes. This event does not fire for purges performed by the cleanup task or for direct store-level deletes, so it reflects intentional application-driven deletions only.
A throwing listener aborts the operation before any store mutation, so listeners can also act as guards.
Application Data on Executions
Section titled “Application Data on Executions”Listeners can attach application-defined data to an execution at creation or update time and read it back later, including during print load. This lets you persist context that was true when the report was filled, such as tenant identity, originating user, or domain selectors, and recover it without re-deriving it on load.
This mechanism is intended for small structured values that describe the execution. Large payloads or binary blobs belong elsewhere.
Virtualization
Section titled “Virtualization”JasperReports Library virtualizers move pages of a filled JasperPrint out of the JVM heap. Some compress pages in memory, others swap them to disk. Without a virtualizer, every page of a filled report stays in heap, which can exhaust memory for large reports.
Typically, an application configures a single global virtualizer and applies it to every fill. ReportShell lets the application resolve a virtualizer per execution through the lifecycle events instead. This makes it practical to keep a pool of virtualizers and pick one based on tenant, report category, or expected size.
Virtualizers plug in at two points:
- At fill time, inside
creatingorupdating, by settingJRParameter.REPORT_VIRTUALIZERon the operation’s parameter values. - At load time, inside
printLoading, by setting the virtualizer on the event so the framework forwards it to the print loader.
Every create and every update produces a new print document, filled with the virtualizer supplied in that operation’s own lifecycle event.
For the stock JasperReports virtualizers (JRGzipVirtualizer, JRFileVirtualizer, JRSwapFileVirtualizer), page data is serialized inline into the print stream during save. The load side can therefore reconstruct pages regardless of which stock virtualizer flavor is supplied at load time, and regardless of whether its swap directory matches the one used at fill. The load-time virtualizer governs future eviction behavior after load, that is, where pages swap to once they are touched, rather than the ability to read the print at all.
Strict matching between fill-time and load-time instances is only required for custom virtualizers that store page data externally and serialize only references such as a handle or file pointer. In that case the same external storage and a compatible virtualizer instance must be available at load, otherwise reconstruction fails.
The framework does not persist the virtualizer itself. Only the execution record and the print file are stored. The recommended pattern is still to record which virtualizer flavor was used at fill time as application data on the execution, and to read it back during printLoading to resolve the same or a compatible instance. For stock virtualizers this is about behavioral consistency, keeping memory policy and swap-file placement aligned with the original fill. For custom external-storage virtualizers it is mandatory.
A few operational notes:
- Stateless virtualizers such as
JRGzipVirtualizerare the easiest to manage because there is no external file to keep alive between fill and load. JRSwapFileVirtualizerkeeps page data in a swap file on the host that performed the fill until the print is serialized to the store. If the JVM dies before that serialization completes, the print is lost because pages were not yet flushed inline. Once the print has been persisted, the original swap file is no longer needed to read it back.- Reuse virtualizer instances where the implementation supports it rather than allocating one per fill.
Related Topics
Section titled “Related Topics”- Executions REST API - Create, update, and delete executions and retrieve print metadata over HTTP
© 2026 Bivektor Inc. All rights reserved. ReportShell™
is a trademark of Bivektor, Inc.
Questions? Email us at reportshell@bivektor.com.
JasperReports® and Jaspersoft® are trademarks of Cloud Software Group, Inc. and/or its subsidiaries. Eclipse BIRT™ and BIRT™ are trademarks of the Eclipse Foundation. Spring® is a trademark of Broadcom Inc. and/or its subsidiaries. React is a trademark of Meta Platforms, Inc. ReportShell and Bivektor, Inc. are not affiliated with, endorsed by, sponsored by or otherwise associated with the owners of the JasperReports®, Jaspersoft®, Spring®, Eclipse BIRT™, BIRT™ or React marks. Any reference to these or other trademarks on this site is made solely for informational, descriptive, comparative and interoperability purposes.