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 servlet | Mounts at | Mixin / child forms | Module |
|---|---|---|---|
JspServlet | /jsp/* | JspMixin / JspResource | juneau-rest-server-view-jsp |
FreemarkerServlet | /freemarker/* | FreemarkerMixin / FreemarkerResource | juneau-rest-server-view-freemarker |
MustacheServlet | /mustache/* | MustacheMixin / MustacheResource | juneau-rest-server-view-mustache |
ThymeleafServlet | /thymeleaf/* | ThymeleafMixin / ThymeleafResource | juneau-rest-server-view-thymeleaf |
StaticFilesServlet | /static/* | StaticFilesMixin / StaticFilesResource | juneau-rest-server |
VersionServlet | /version/* | VersionMixin / VersionResource | juneau-rest-server |
AdminServlet | /admin/* | AdminMixin / AdminResource | juneau-rest-server |
FaviconServlet | /favicon.ico | FaviconMixin / FaviconResource | juneau-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 aRawTemplateDispatcher. The matchingViewResourcebase does the same for the child form, and the view mixins (JspMixin, ...) share the same dispatcher workers, so raw.jsp/.ftlh/.mustache/.htmldispatch 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 —
RawTemplateDispatchervsViewRenderer. These are different contracts.ViewRendereris the response-processor marker that rendersView-typed@RestOpreturn values.RawTemplateDispatchercovers the raw file-serving path (serving a template directly by its trailing request path). The view dispatcher workers implementRawTemplateDispatcher; the renderers implementViewRenderer.
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:
| Class | Flavor | Composition primitive | Mount |
|---|---|---|---|
HealthMixin | Mixin | @Rest(mixins=HealthMixin.class) | Grafted into the host's own URL space at /healthz, /readyz, /livez. |
HealthServlet | Standalone | @Rest(paths={"/healthz","/readyz","/livez"}) | Auto-mounted at those exact URLs — the Servlet bean HealthProbeConfiguration.healthProbeServlet() returns one. |
HealthResource | Child | @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.