Skip to main content

REST Server — Standalone vs Mixin Resources

Several of Juneau's bundled convention and operations classes are dual-use: they're useful both as a mixin composed into a host resource via @Rest(mixins=...), as a child resource composed via @Rest(children=...), and as a standalone top-level servlet deployed on their own. Each dual-use capability ships in up to three forms that behave identically: <Name>Mixin, <Name>Resource, and <Name>Servlet.

The three shapes​

Mixin form (<Name>Mixin)​

A mixin pins its operations at an op-level sub-path so it can be grafted into a host mounted at /:

@Rest(mixins=JspMixin.class)
public class AppResource extends RestServlet { /* ... */ }
// JSPs now served at /jsp/* alongside the host's own endpoints.

Internally the mixin declares something like @RestGet(path="/jsp/*") on its handler. This is the right shape for composition, but it doesn't work if you try to deploy the class by itself at a top-level path: mounting a /jsp/*-pinned resource at /jsp/* would double the prefix to /jsp/jsp/....

Child form (<Name>Resource)​

A child resource carries a class-level @Rest(path="/<token>") and is composed under a parent's subtree via @Rest(children=...):

@Rest(children=AdminResource.class)
public class AppResource extends RestServlet { /* ... */ }
// Admin operations now served under /admin/* of the parent.

Standalone form (<Name>Servlet)​

A standalone companion moves the mount to the class level via @Rest(paths="/<token>/*") and pins its ops at /* (or the op's own sub-path), so it serves at a clean sibling top-level path with no prefix-doubling:

// Microservice: auto-mounted at /jsp/* by the Jetty auto-discovery loop.
@Bean Servlet jsp() { return new JspServlet(); }

The standalone servlet registers as a sibling of the application's other servlets (/rest/*, /jsp/*, /static/*, ...), not as a child composed under a host root.

Dual-use catalog​

Standalone servletMounts atMixin / child formsModule
JspServlet/jsp/*JspMixin / JspResourcejuneau-rest-server-view-jsp
FreemarkerServlet/freemarker/*FreemarkerMixin / FreemarkerResourcejuneau-rest-server-view-freemarker
MustacheServlet/mustache/*MustacheMixin / MustacheResourcejuneau-rest-server-view-mustache
ThymeleafServlet/thymeleaf/*ThymeleafMixin / ThymeleafResourcejuneau-rest-server-view-thymeleaf
StaticFilesServlet/static/*StaticFilesMixin / StaticFilesResourcejuneau-rest-server
VersionServlet/version/*VersionMixin / VersionResourcejuneau-rest-server
AdminServlet/admin/*AdminMixin / AdminResourcejuneau-rest-server
FaviconServlet/favicon.icoFaviconMixin / FaviconResourcejuneau-rest-server

SeoMixin, WellKnownMixin, RouteIndexMixin, EchoMixin, and the API-docs pack (Swagger / SwaggerUi / OpenApi / Redoc) remain mixin-only: they're only meaningful composed into a host, so they get no standalone companion.

Shared logic — the forms can't drift​

The companions are deliberately thin. They never re-implement behavior; each form delegates to a shared, flavor-neutral worker (JspDispatcher, AdminProvider, VersionProvider, FaviconProvider, ...) so there is a single source of truth.

  • View servlets share an abstract base, ViewServlet (in org.apache.juneau.rest.server.view), whose single @RestGet(path="/*") op delegates to a RawTemplateDispatcher. The matching ViewResource base does the same for the child form, and the view mixins (JspMixin, ...) share the same dispatcher workers, so raw .jsp / .ftlh / .mustache / .html dispatch runs through the same code in every form.

    @Rest(paths="/jsp/*", responseProcessors=JspViewRenderer.class)
    public class JspServlet extends ViewServlet {
    private final JspDispatcher worker = JspDispatcher.create().build();
    @Override protected RawTemplateDispatcher dispatcher() { return worker; }
    }
  • Non-view servlets (StaticFilesServlet, VersionServlet, AdminServlet) hold their matching worker (AdminProvider, VersionProvider, ...) and forward each op to it verbatim.

Note — RawTemplateDispatcher vs ViewRenderer. These are different contracts. ViewRenderer is the response-processor marker that renders View-typed @RestOp return values. RawTemplateDispatcher covers the raw file-serving path (serving a template directly by its trailing request path). The view dispatcher workers implement RawTemplateDispatcher; the renderers implement ViewRenderer.

Health probes: three flavors​

The built-in health/readiness/liveness probe capability (org.apache.juneau.rest.server.health) follows the same three-form pattern as the catalog above, but it was designed from the start as three independent classes, one per composition primitive, all delegating to the same HealthAggregator worker bean so the forms can't drift from each other:

ClassFlavorComposition primitiveMount
HealthMixinMixin@Rest(mixins=HealthMixin.class)Grafted into the host's own URL space at /healthz, /readyz, /livez.
HealthServletStandalone@Rest(paths={"/healthz","/readyz","/livez"})Auto-mounted at those exact URLs — the Servlet bean HealthProbeConfiguration.healthProbeServlet() returns one.
HealthResourceChild@Rest(path="/health"), composed via @Rest(children=HealthResource.class)Under a parent's /health subtree, e.g. /health/healthz.

Like the rest of the catalog, the health family maps each of the three composition primitives this topic and Mixins and Multi-Mount Paths document — mixins=, paths=, and children= — onto its own dedicated class. Picking the flavor that matches how you want to compose probes into your app is the API. See Health / Readiness / Liveness Probes for the worked examples of the mixin and standalone options.

See also​