Class MustacheMixin

java.lang.Object
org.apache.juneau.rest.server.view.mustache.MustacheMixin

public class MustacheMixin extends Object
Mixin that wires Mustache view-rendering onto any Juneau REST resource.

Compose into a host resource via @Rest(mixins=MustacheMixin.class); the host then:

  1. Gains a default mount at /mustache/* that serves raw Mustache templates from the importer's classpath by asking the configured MustacheFactory to compile and render them with no scope (raw render path; callers who want attributes use MustacheView from a typed handler instead).
  2. Routes the host's own @RestOp-method return values of type MustacheView through the Mustache engine automatically. MustacheMixin declares @Rest(mergeResponseProcessorsIntoHost=true), so a plain @Rest(mixins=MustacheMixin.class) folds its MustacheViewRenderer into the host's own response-processor chain — no mergeIntoHost or explicit responseProcessors= on the host needed.
Composition example (microservice):

@Rest(path="/app", mixins=MustacheMixin.class) public class AppResource extends RestServlet { @Bean MustacheMixin mustache() { return MustacheMixin.create() .basePath("/templates/") .templateSuffix(".mustache") .build(); } @RestGet("/hello/{name}") public View hello(@Path String name) { return MustacheView.of("hello").attr("name", name); } }

Configurable mount path:

The default mount /mustache/* can be overridden via the SVL variable ${juneau.mustache.path:mustache} — set via system property (-Djuneau.mustache.path=views), environment variable (JUNEAU_MUSTACHE_PATH=views), or Config key (juneau.mustache.path = views) to change the runtime mount without subclassing. Resolution happens once at RestContext construction time (SVL resolution in @RestOp(path)).

Mixin-only deployment:

This resource is designed for composition via @Rest(mixins=...). The mount path is pinned at the op level by @RestGet(path="/{juneau.mustache.path:mustache}/*") on render(java.lang.String, org.apache.juneau.rest.server.RestRequest, org.apache.juneau.rest.server.RestResponse); a class-level @Rest(paths=...) declaration would be silently ignored under the mixin pattern (see @Rest(paths) Javadoc).

Choosing a MustacheFactory:

The juneau-rest-server-view-mustache module ships only the mustache.java compiler API in provided scope. No factory bean is bundled. The bridge resolves a MustacheFactory at first use from three sources, in order:

  • User-supplied bean: register a @Bean MustacheFactory (Spring) or BasicBeanStore.put(MustacheFactory.class, factory) (microservice). Allows full control of resolvers, object handlers, and encoders.
  • Bridge default: when nothing is registered, the bridge constructs a default DefaultMustacheFactory anchored on a resource root derived from basePath (leading + trailing slashes trimmed; a "/" base yields a no-prefix factory). With basePath("/templates/"), the factory resolves template names like "hello.mustache" as classpath resource templates/hello.mustache.

mustache.java's DefaultMustacheFactory caches compiled templates internally. The bridge-default factory honors cacheTemplates(...) (default true — production-safe): set false to have the bridge rebuild the factory from a fresh, empty compile-cache on every render (dev hot-reload). Consumers who supply their own @Bean MustacheFactory manage its caching themselves — the flag only affects the bridge-default factory.

When no Mustache engine is on the classpath, the renderer surfaces MustacheViewRenderer.NO_ENGINE_DIAGNOSTIC naming the missing dependency.

Template suffix:

Unlike the Thymeleaf bridge (which always appends .html via the engine resolver's suffix setting), mustache.java has no resolver-suffix concept — the literal template name is what factory.compile(...) sees. The templateSuffix(...) builder knob fills the gap: when non-blank, the bridge appends the configured suffix to template names that don't already end with it (idempotent), so callers can write MustacheView.of("hello") or request /mustache/hello and have it resolve to hello.mustache on the classpath.

Multiple base paths:

A host with /templates/ and /admin/templates/ template trees registers two MustacheMixin beans, each mounted via a subclass declaring its own op-level @RestGet(path=...) override and its own basePath. See the MustacheMixin_MockRest_Test test in juneau-integration-tests for the canonical pattern.

OpenAPI surface:

The greedy /* handler is not API-meaningful and is excluded from generated Swagger / OpenAPI specs via @OpSwagger(ignore=true).

See Also:
Since:
10.0.0
  • Field Details

  • Constructor Details

  • Method Details

    • create

      public static MustacheMixin.Builder create()
      Creates a new builder.
      Returns:
      A new builder.
    • getBasePath

      public String getBasePath()
      Returns the base path under which template resources are resolved.
      Returns:
      The base path. Never null.
    • getTemplateSuffix

      Returns the configured template-name suffix.
      Returns:
      The template suffix. Never null.
    • isCacheTemplates

      public boolean isCacheTemplates()
      Returns whether the bridge's default factory caches compiled templates.
      Returns:
      The cache flag. Defaults to DEFAULT_CACHE_TEMPLATES.
    • applyTemplateSuffix

      Appends getTemplateSuffix() to name if not already present (idempotent).
      Parameters:
      name - The template name. Must not be null.
      Returns:
      The template name with the configured suffix appended (if applicable).
    • resolveMustacheFactory

      public com.github.mustachejava.MustacheFactory resolveMustacheFactory(RestRequest req)
      Resolves the active MustacheFactory via the shared MustacheDispatcher worker.
      Parameters:
      req - The current REST request.
      Returns:
      The active Mustache factory. Never null.
    • render

      public void render(@Path("/*") String path, RestRequest req, RestResponse res) throws IOException, BasicHttpException
      [GET /mustache/*] — render a raw template through the Mustache engine.

      The @Path("/*") String path captures the multi-segment trailing remainder (e.g. a request for /mustache/about.mustache matches the mount with path = "about.mustache"). Delegates to the shared MustacheDispatcher worker (path-traversal hardening, suffix application, factory resolution, and render).

      Missing template surfaces as the engine's own MustacheNotFoundException (HTTP 500 from the renderer's catch-all); missing engine surfaces as MustacheViewRenderer.NO_ENGINE_DIAGNOSTIC.

      Parameters:
      path - The trailing path segment after /mustache/ (the template name relative to the configured base path; configured suffix appended if missing).
      req - The current REST request.
      res - The current REST response.
      Throws:
      IOException - If the underlying response writer fails.
      BasicHttpException - On boundary violation (403), missing engine (500), or render failure (500).