REST Server — Self-Registration
A Juneau servlet can declare where it wants to be mounted via the top-level path chain —
@Rest(paths=...) or a getPaths() override —
rather than having the deployer hard-code a URL pattern at registration time. Three mechanisms read
that self-declaration and register the servlet accordingly, one per runtime.
All three resolve the mount path through the same resolver:
String[] paths = RestContext.resolveTopLevelPaths(servletClass, servletInstance, beanStore);
resolveTopLevelPaths(...) walks the precedence chain programmatic override → getPaths() getter →
@Rest(paths) annotation default (with SVL substitution on annotation elements, so $C{...} /
${env} templates resolve against the supplied bean store). It reads the top-level chain
(paths() / getPaths()), never path() (which is for child-composition). Because every
mechanism funnels through this one method, mount paths are identical across runtimes.
Microservice (Jetty)
No new API — the existing JettyServerComponent auto-discovery loop already calls
resolveTopLevelPaths(...) for each @Bean Servlet it finds and mounts it at the resolved paths.
A standalone companion (or any @Rest(paths=...) servlet) is mounted simply by exposing it as a
bean:
@Bean Servlet version() { return new VersionServlet(); } // auto-mounted at /version/*
Spring Boot
Opt-in auto-configuration
JuneauRestAutoConfiguration (in juneau-rest-server-springboot, registered via
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports) walks every
RestServlet bean in the application context and mounts each at its self-declared path.
It is gated behind a property (no default — matchIfMissing=false):
juneau.rest.auto-register=true
When unset (or any value other than true) the auto-configuration contributes nothing and servlet
registration stays fully manual. When active it:
- Skips any servlet instance already wired into a manual
ServletRegistrationBean(explicit registrations always win — no double-mounts). - Resolves each servlet's mount paths via
resolveTopLevelPaths(...)using the application's registeredBeanStore. - Registers the servlet at each resolved path. Servlets that resolve to no top-level paths (pure mixins / child-composed resources) are skipped.
Explicit factory helper
When you'd rather register servlets yourself but still want the mount path to come from the servlet (not a hard-coded string), use the always-available factory helper:
@Bean
ServletRegistrationBean<?> jspServlet(BeanStore beanStore) {
return JuneauServletRegistrations.forServlet(new JspServlet(), beanStore);
}
JuneauServletRegistrations.forServlet(...) derives the registration's URL mappings from the
servlet's @Rest(paths) / getPaths(). A servlet that resolves to no top-level paths is rejected
with an IllegalArgumentException.
Non-Spring (web.xml / external WAR)
For plain Servlet-spec deployments, juneau-rest-server ships a
jakarta.servlet.ServletContainerInitializer, JuneauRestServletContainerInitializer (discovered
via META-INF/services/jakarta.servlet.ServletContainerInitializer). Through
@HandlesTypes(RestServlet.class) the container hands it every RestServlet on the webapp
classpath; it auto-mounts each concrete, @Rest-annotated, no-arg-instantiable servlet that
self-declares top-level paths.
It is opt-in via a context init parameter (parity with the Spring Boot property), so it never
collides with servlets the deployer declared manually in web.xml:
<context-param>
<param-name>juneau.rest.auto-register</param-name>
<param-value>true</param-value>
</context-param>
Cross-runtime parity
| Runtime | Mechanism | Activation | Reads |
|---|---|---|---|
| Jetty microservice | JettyServerComponent auto-discovery | always (for @Bean Servlet) | resolveTopLevelPaths(...) |
| Spring Boot | JuneauRestAutoConfiguration | juneau.rest.auto-register=true | resolveTopLevelPaths(...) |
| Spring Boot | JuneauServletRegistrations.forServlet(...) | explicit @Bean | resolveTopLevelPaths(...) |
web.xml / WAR | JuneauRestServletContainerInitializer | juneau.rest.auto-register context-param | resolveTopLevelPaths(...) |