juneau-microservice-tomcat
| Maven | |
|---|---|
| Jar | juneau-microservice-tomcat-10.0.0.jar |
| OSGi | org.apache.juneau.microservice.tomcat_10.0.0.jar |
| Links | Maven Central · Javadocs · Source |
Contents/Features
juneau-microservice-tomcat is a Tomcat-embedded microservice launcher that mirrors the developer experience of
juneau-microservice-jetty but runs Juneau REST resources inside an
embedded Apache Tomcat servlet container instead of Jetty. It is part of the juneau-microservice
family and inherits all of the base microservice features: configuration files, console commands, lifecycle events,
and inject-aware bootstrap.
Choose this module when:
- Your organisation's production runtime standardises on Tomcat.
- You need Jakarta EE servlet-container features that are provided by Tomcat but not by Jetty's minimal embedded mode.
- You want to share WAR-deployment configurations between your embedded development experience and a full Tomcat server.
Package overview
The juneau-microservice-tomcat library consists of the following classes:
Getting Started
Add the dependency above, then boot the microservice from your main method:
public class App {
public static void main(String[] args) throws Exception {
Microservice
.create()
.args(args)
.configurations(TomcatConfiguration.class, AppConfig.class)
.build()
.start()
.startConsole()
.join();
}
@Configuration
public static class AppConfig {
@Bean
Servlet rootResources() { return new RootResources(); }
}
}
RootResources (or any @Bean Servlet whose class carries @Rest) is auto-mounted by TomcatServerComponent
at the path declared in @Rest(path = "...").
No one-liner facade — unlike the Jetty module,
juneau-microservice-tomcatdoes not ship aTomcatMicroserviceconvenience class. UseMicroservice.create().configurations(TomcatConfiguration.class, ...)directly.
Configuration
TomcatServerComponent reads its runtime settings from juneau.cfg (on the classpath or working directory)
under the [Tomcat] section.
Port selection
The embedded Tomcat connector resolves its port from the following sources, in priority order:
- A
@Bean TomcatSettingscontributed by a user@Configurationclass. - The
Tomcat/portentry injuneau.cfg(comma-separated list of candidate ports;0= random). - A
Tomcat-Portentry in the jar manifest file. - The hard-coded default
8000.
The first available port in the list is used.
[Tomcat]
port = 8080
To specify multiple candidate ports (first available wins):
[Tomcat]
port = 8080,8081,8082
Servlet auto-mounting from config
Servlets can be registered directly from the config file without writing @Configuration code:
[Tomcat]
# Comma-separated list of RestServlet subclass names, mounted at their @Rest(path).
servlets = com.example.RootResources, com.example.AdminResources
# Path-keyed map of arbitrary Servlet class names.
servletMap = /api = com.example.ApiServlet
# Key/value pairs added to the root servlet context as attributes.
servletAttributes = myKey = myValue
Graceful-shutdown knobs
Two knobs in the [Tomcat] section (both in milliseconds) control graceful shutdown.
See Graceful Shutdown & Readiness Gating for full details.
[Tomcat]
# Bounded drain timeout: how long to wait for in-flight requests after pausing the connector.
# Defaults to 30000 (30s) when unset.
stopTimeout = 30000
# Settle delay between the /readyz 503 flip and connector pause.
# Defaults to 0 when unset.
shutdownSettleDelay = 0
Programmatic settings
Contribute a @Bean TomcatSettings from your own @Configuration class to override defaults programmatically:
import java.time.Duration;
import org.apache.juneau.commons.inject.Bean;
import org.apache.juneau.commons.inject.Configuration;
import org.apache.juneau.microservice.tomcat.TomcatSettings;
@Configuration
public class MyAppConfig {
@Bean
Servlet rootResources() { return new RootResources(); }
@Bean
TomcatSettings tomcatSettings() {
return TomcatSettings.create()
.ports(8080, 0) // try 8080, else random
.baseDir("/opt/myapp/tomcat-work") // persistent Catalina base dir
.stopTimeout(Duration.ofSeconds(45))
.shutdownSettleDelay(Duration.ofSeconds(2))
.build();
}
}
Any value left null in TomcatSettings falls back to the corresponding juneau.cfg entry, then to the default.
TomcatSettings reference
| Builder method | juneau.cfg key | Default | Description |
|---|---|---|---|
ports(int...) | Tomcat/port | 8000 | Candidate ports in priority order; first available is used. 0 = random. |
baseDir(String) | — | auto temp dir | Persistent Catalina base directory. When null, a temp dir is created on start and deleted on stop. |
stopTimeout(Duration) | Tomcat/stopTimeout (ms) | 30s | Bounded time to drain in-flight requests after pausing the connector. |
shutdownSettleDelay(Duration) | Tomcat/shutdownSettleDelay (ms) | 0 | Pause between /readyz 503 flip and connector pause; lets a load balancer observe the 503 before draining begins. |
Customizing and extending
Customization follows the same @Bean-override pattern as the Jetty module. User-supplied @Bean definitions
win over the defaults contributed by TomcatConfiguration.
| Override | Replace by contributing |
|---|---|
| Tomcat server itself | @Bean Tomcat (skips the factory entirely). |
| Server factory | @Bean TomcatServerFactory — used when no @Bean Tomcat is supplied. |
| Ports / base dir / shutdown timing | @Bean TomcatSettings |
| Servlet auto-mounts | @Bean Servlets annotated with @Rest — mounted at @Rest(path = "..."). |
Custom server factory
Implement TomcatServerFactory to take full control of the Tomcat instance before it starts:
@Configuration
public class MyAppConfig {
@Bean
TomcatServerFactory tomcatServerFactory() {
return baseDir -> {
Tomcat tomcat = new Tomcat();
tomcat.setBaseDir(baseDir.getAbsolutePath());
// configure connectors, realms, valves, etc.
tomcat.addContext("", baseDir.getAbsolutePath());
tomcat.getHost().setAutoDeploy(false);
return tomcat;
};
}
}
Hooking into the lifecycle
Implement MicroserviceListener and expose it as a named @Bean — it participates in the microservice's
start() / stop() fan-out alongside TomcatServerComponent:
@Configuration
public class MyAppConfig {
@Bean
Servlet rootResources() { return new RootResources(); }
@Bean(name = "lifecycle")
MicroserviceListener lifecycle() {
return new BasicMicroserviceListener() {
@Override
public void onStart(Microservice ms) {
// custom start-up logic
}
@Override
public void onStop(Microservice ms) {
// custom shutdown logic
}
};
}
}
Registering servlet filters
Contribute a ServletFilterRegistration bean to mount a jakarta.servlet.Filter before the server starts. The same
type works on Jetty and on Tomcat.
@Bean(name="consoleBoundary")
ServletFilterRegistration consoleBoundary() {
return ServletFilterRegistration.create(new PetstoreConsoleBoundary())
.urlPatterns("/console/*")
.dispatcherTypes(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC, DispatcherType.ERROR)
.order(ServletFilterRegistration.HIGHEST_PRECEDENCE)
.build();
}
- URL patterns follow the Servlet specification: a prefix (
/api/*), an extension (*.json), an exact path, or""for the context root. At least one is required, and a malformed pattern is rejected when the registration is built. - Dispatcher types default to
REQUESTonly.dispatcherTypes(...)replaces the default, so listREQUESTexplicitly when you want it. - Order. Lower
ordervalues run first. Filters with the same order run by bean name, so give each registration its own@Bean(name=...).@Orderon the bean method does not affect filter order. The default is0, which runs after the auto-mountedAuthFilterChainatServletFilterRegistration.AUTH_ORDER(-100). A filter that must reject a request before authentication, such as a CSRF boundary, usesHIGHEST_PRECEDENCEand a narrow pattern; on/*it would also intercept authentication redirects and challenges. AuthFilterChain. A bean of that type is mounted automatically. To change its dispatchers or order, register the injected bean instance in your ownServletFilterRegistration; that registration replaces the automatic one.- Validation. The same filter instance cannot be registered twice, and explicit
name(...)values must be unique.
The addFilter(Filter, String...) overloads on the server component are unchanged and still map REQUEST only.
Accessing the Tomcat server at runtime
TomcatServerComponent is published into the bean store at start-up and can be retrieved to inspect or
manipulate the running server:
TomcatServerComponent tomcat = Microservice
.getInstance()
.getBeanStore()
.getBean(TomcatServerComponent.class)
.orElseThrow();
URI uri = tomcat.getURI(); // e.g. http://myhost:8080
int port = tomcat.getPort();
Tomcat server = tomcat.getServer(); // the raw org.apache.catalina.startup.Tomcat instance
Logging
TomcatLogger bridges Tomcat's internal JULI logging to the microservice's java.util.logging infrastructure
so all Tomcat log output flows to the same log file configured under [Logging] in juneau.cfg. The level
mapping is: trace→FINEST, debug→FINE, info→INFO, warn→WARNING, error/fatal→SEVERE.
Comparison with juneau-microservice-jetty
Both launchers share the same Microservice + @Configuration + @Bean Servlet programming model and the
same lifecycle contracts.
| Feature | Jetty | Tomcat |
|---|---|---|
| One-liner convenience facade | JettyMicroservice.run(args, servlet) | none — use Microservice.create() directly |
| Configuration class | JettyConfiguration | TomcatConfiguration |
| Server factory interface | JettyServerFactory | TomcatServerFactory |
| Settings bean | JettySettings | TomcatSettings |
| Lifecycle component | JettyServerComponent | TomcatServerComponent |
| Config section | [Jetty] | [Tomcat] |
| Default port | 10000 | 8000 |
| Server descriptor file | jetty.xml (classpath / working dir) | none |
| Graceful shutdown | server.setStopTimeout(...) + stop() | connector.pause() → drain active count → stop() / destroy() |
| Catalina base directory | n/a | auto temp dir (configurable via TomcatSettings.baseDir) |
See Also
- juneau-microservice — core microservice framework shared by all launchers
- juneau-microservice-jetty — Jetty-based parallel launcher
- Graceful Shutdown & Readiness Gating — zero-downtime k8s shutdown contract (identical between Jetty and Tomcat)
- Health Probes —
/healthz,/readyz,/livezprobe surface - REST Server — the REST server API that both launchers host