interface AssetManager

Describes the API surface of an Asset Manager, which controls access to embedded server-side assets such as CSS and JS documents.

The asset manager is responsible for maintaining a runtime registry of all available assets, which is typically built at server startup. Assets should be held in memory so they may easily be resolved, dynamically transformed, and ultimately served to end-users through either user-defined controllers or the built-in asset controller.

Asset descriptors

During the app build, the Elide build tools package scripts for use with SSR or client-side serving. After packaging assets from internal and external builds, protocol buffer descriptors are computed and embedded in the application, which describe how each asset should be served, addressed, and treated with regard to dependencies.

At server startup, the encoded descriptors are loaded, assets are interpreted and loaded, and the server refers to the resulting mapping when called upon to serve assets.


Static assets do exist on disk, technically, but when embedded in an application JAR or native image, they are automatically zipped and inlined into the application binary. Therefore, OS-level tools like sendfile aren't an option.

However, Micronaut and KotlinX Coroutines both have built-in IO scheduling support, which is usually backed by a pool of cached POSIX threads. This offers a good alternative for offloading I/O from a user-defined request handler.

Customizing the asset system

The AssetManager is a simple interface which mediates between an AssetResolver and AssetReader implementation pair to serve assets at mapped HTTP paths. In some cases, the developer may want to customize this behavior, either in the way asset paths are translated or interpreted (via a custom AssetResolver), or the way asset content is loaded and returned (via a custom AssetReader). Both can be used at once if needed.

To customize a given asset system component, use the Replaces annotation from Micronaut's context module. For example:


import elide.server.assets.AssetReader;
import io.micronaut.context.annotation.Replaces;

public class MyCoolAssetReader: AssetReader {
// (impl here)

The AssetManager participates in the DI container, so the developer only needs to provide a component definition. Later, when an AssetManager instance is requested by the app, the main implementation will load and use the developer's custom implementation.

See also

which is responsible for checking, translating, and loading asset paths.

which is responsible for reading asset content efficiently.

for the generic return value model leveraged by AssetManager.

for the symbolic asset reference model leveraged by AssetManager.



Link copied to clipboard
abstract val logging: Logger

Logger which should be used to emit not-found warnings and other messages from the asset manager implementation which is live for the current server lifecycle.

Link copied to clipboard
abstract val reader: AssetReader

Asset reader which is in use for this asset manager; responsible for translating an absolute asset resource path into a stream of actual resource content.


Link copied to clipboard

Resolve an AssetPointer for the specified asset module ID; if none can be located within the current set of live server assets, return null.

Link copied to clipboard
abstract fun linkForAsset(module: AssetModuleId, overrideType: AssetType? = null): String

Generate a relative link to serve the asset specified by the provided module ID; the link is built from the active configured asset prefix, plus the "asset tag," which is a variable-length cryptographic fingerprint of the asset's content.

Link copied to clipboard
abstract suspend fun renderAssetAsync(request: HttpRequest<*>, asset: ServerAsset): Deferred<StreamedAssetResponse>

Responsible for converting a known-good asset held by the server into an efficient StreamedAssetResponse which serves the asset to the invoking client.

Link copied to clipboard
open fun resolve(request: HttpRequest<*>, moduleId: String? = null): ServerAsset?

Resolve the asset requested by the provided HTTP request; if the corresponding file cannot be found, return null, and otherwise, throw an error.

Link copied to clipboard
open suspend fun serveAsync(request: HttpRequest<*>, moduleId: String? = null): Deferred<StreamedAssetResponse>

Asynchronously produce an HTTP response which serves the asset described by the provided request; if the asset in question cannot be located, serve a 404 Not Found, and for any other error, serve a 500 Internal Server Error.

Link copied to clipboard
open fun serveNotFoundAsync(request: HttpRequest<*>): Deferred<StreamedAssetResponse>

Serve a response of status HTTP 404 (Not Found), in response to a request for an asset which could not be located by the built-in asset system.