Convention-Endpoints Mixin Pack
The Juneau REST server ships with a four-class convention-endpoints mixin pack in
org.apache.juneau.rest.server.convention
covering the small set of de-facto-standard URLs that every public-facing service eventually has
to wire up by hand: the browser favicon, the SEO contracts (robots.txt, sitemap.xml), a
JSON deployment-introspection endpoint, and the RFC 8615 /.well-known/* discovery root. Each
mixin is a single-purpose @Rest-annotated resource designed to be grafted into your servlet via
@Rest(mixins=...) — pick the URLs you want,
configure them via a @Bean factory, and leave the rest unmounted.
The four mixins at a glance
| Mixin | Default paths | Default body | Why it exists |
|---|---|---|---|
FaviconMixin | /favicon.ico | Juneau-branded juneau-favicon.ico shipped on the framework classpath. | Stops every browser from logging a 404 on /favicon.ico. |
SeoMixin | /robots.txt, /sitemap.xml | User-agent: *\nDisallow: /\n (deny-all) and an empty <urlset>. | RFC 9309 + sitemaps.org compliance. Deny-all default is intentional — opt into indexing explicitly. |
VersionMixin | /version, /info, /about | JSON map sourced from META-INF/MANIFEST.MF, git.properties, and the running JVM version. | Deployment introspection. All three URLs return the same payload (they are synonyms). |
WellKnownMixin | /.well-known/security.txt | None — returns 404 Not Found until the importer configures a body. | RFC 9116 security.txt discovery; absence is itself meaningful, so v1 ships with no placeholder. |
All four endpoints carry @OpSwagger(ignore = true)
on their handlers — they show up in the runtime URL space but are excluded from any Swagger /
OpenAPI spec generated by the api-docs mixin pack. Convention URLs
are not API-meaningful, and shipping them in the spec just clutters the contract.
Composing the pack
Each mixin is independent — drop in only the ones you need. The examples below build on plain
RestServlet. If you're building on BasicRestServlet/BasicRestResource instead, note that
FaviconMixin (along with ErrorMixin, HtdocMixin, and StatsMixin) is already baked in by
default — re-declaring mixins=FaviconMixin.class on the subclass is redundant but harmless, since
the mixin walk collects mixins in parent-to-child order and dedupes by class identity (see
Mixin Sub-Contexts for the walk details).
The plan-A composition mounts all four with builder-driven configuration:
@Rest(
path = "/api",
mixins = {
FaviconMixin.class,
SeoMixin.class,
VersionMixin.class,
WellKnownMixin.class
}
)
public class ApiResource extends RestServlet {
@RestGet("/items") public List<Item> items() { ... }
@Bean FaviconMixin favicon() {
return FaviconMixin.create().bytes(myLogoBytes).build();
}
@Bean SeoMixin seo() {
return SeoMixin.create()
.robotsAllow("*", "/")
.sitemapEntry("https://example.com/api/items")
.build();
}
@Bean VersionMixin version() {
// Defaults read MANIFEST.MF + git.properties + JVM version when no entries are set;
// override programmatically here:
return VersionMixin.create()
.entry("name", "my-app")
.entry("version", "1.2.3")
.fromGitProperties()
.fromJavaVersion()
.build();
}
@Bean WellKnownMixin wellKnown() {
return WellKnownMixin.create()
.securityTxt("Contact: security@example.com\nExpires: 2027-01-01T00:00:00Z\n")
.build();
}
}
The framework's mixin walk picks up each @Bean <MixinClass> factory before falling back to
no-arg construction (see Mixin Sub-Contexts for the
underlying lookup), so the host controls the configuration end-to-end without subclassing.
Mixins without a @Bean factory get default behavior: FaviconMixin serves the framework
icon; SeoMixin returns deny-all robots and an empty sitemap; VersionMixin
reads MANIFEST.MF + git.properties from the framework classloader; WellKnownMixin
returns 404 on /.well-known/security.txt.
Standalone deployment
Each mixin is a fully-fledged @Rest-annotated resource and can also be subclassed and mounted as
its own top-level servlet:
@Rest(paths = {"/favicon.ico"})
public class FaviconResource extends FaviconMixin { }
Both deployment styles (mixin into an existing servlet vs. mount as a sibling servlet) work the
same way under Spring Boot and under the Jetty microservice — the
VersionMixin_JettyMicroservice_Test
and
VersionMixin_Springboot_Test
parity tests cover both paths end-to-end.
Per-mixin notes
FaviconMixin
Cache-Control: max-age=2592000, public(30 days) by default — favicons rarely change and browsers re-fetch frequently when uncached. Override viaBuilder.cacheControl(String).Content-Type: image/x-icon.- Builder methods:
bytes(byte[]),classpath(String),cacheControl(String).bytesandclasspathare mutually exclusive — whichever is set last wins. Aclasspathpath that resolves to a missing resource silently falls back to the framework default.
SeoMixin
/robots.txtcontent type:text/plain; charset=UTF-8. Default body is the deny-allUser-agent: *\nDisallow: /\n. Replace viaBuilder.robotsAllow(String, String...)/robotsDisallow(...)for rule-based policies, orrobotsTxt(String)for a fully-formed body (useful when you needSitemap:/Crawl-delay:directives the rule builder doesn't model)./sitemap.xmlcontent type:application/xml; charset=UTF-8. Default body is an empty<urlset>. Add entries viaBuilder.sitemapEntry(String url)(URL-only) or the four-arg form withlastmod,changefreq, andpriority.
VersionMixin
Content-Type: application/json— format-pinned viaRestResponse.getDirectWriter("application/json")so the endpoint serves JSON even on a vanillaRestServlethost that hasn't wired up JSON serializers explicitly.- All three URLs (
/version,/info,/about) currently return the same JSON payload. Future iterations may differentiate (e.g./info= condensed,/about= full). - Default builder chain:
fromManifest()+fromGitProperties()+fromJavaVersion(). The manifest reader prefers aMETA-INF/MANIFEST.MFwhoseImplementation-Titleis set; if no candidate has one, the first manifest found wins. Spring Boot fat-jar caveat: the no-arg reader uses the framework classloader, so to see the importer's app manifest under a fat jar callBuilder.fromManifest(importerClassLoader)(orBuilder.fromManifest(Manifest)with a pre-loaded manifest). - Programmatic override:
Builder.entry(String, String)/Builder.entries(Map). Calling any builder method suppresses the default chain — you opt into eachfromXxx()call you want.
WellKnownMixin
Content-Type: text/plain; charset=UTF-8.- No default body — per RFC 9116 the file's presence is itself meaningful. If you don't call
Builder.securityTxt(String), the endpoint returns404 Not Found. Document loudly to your ops team. - The class reserves room for future
/.well-known/*entries (OIDC discovery, change-password, etc.); v1 ships onlysecurity.txtto stay scoped.
See also
@Rest(mixins=...)— the underlying composition primitive.- Mixin Sub-Contexts — host-to-mixin
RestContextinheritance and bean-store layering. - Static-Files Mixin — sibling mixin pack for arbitrary classpath-served assets.
- API-Docs Mixin Pack — Swagger / OpenAPI / Redoc mixins.