Eclipse Jetty servlet container providing comprehensive servlet, filter, and listener integration with lifecycle management and dynamic registration support.
—
Eclipse Jetty provides comprehensive monitoring and statistics capabilities through the StatisticsServlet for runtime metrics and JMX support for management and monitoring integration with enterprise management systems.
The StatisticsServlet provides HTTP access to server runtime statistics and performance metrics.
public class StatisticsServlet extends HttpServlet {
// Constructor
public StatisticsServlet();
// Servlet lifecycle
public void init(ServletConfig config);
// Request handling
protected void doGet(HttpServletRequest req, HttpServletResponse resp);
protected void doPost(HttpServletRequest req, HttpServletResponse resp);
}Eclipse Jetty provides JMX management beans for comprehensive monitoring and management.
// JMX management bean for holders (ServletHolder, FilterHolder, ListenerHolder)
public class HolderMBean extends ObjectMBean {
// Provides JMX access to holder properties and lifecycle
}// JMX management bean for filter mappings
public class FilterMappingMBean extends ObjectMBean {
// Provides JMX access to filter mapping configuration
}// JMX management bean for servlet mappings
public class ServletMappingMBean extends ObjectMBean {
// Provides JMX access to servlet mapping configuration
}The DefaultServlet provides resource serving capabilities with built-in monitoring support.
public class DefaultServlet extends HttpServlet implements ResourceFactory, WelcomeFactory {
// Initialization and configuration
public void init();
public String getInitParameter(String name);
// Resource access with monitoring
public Resource getResource(String pathInContext);
public String getWelcomeFile(String pathInContext);
// HTTP method handlers with performance tracking
protected void doGet(HttpServletRequest request, HttpServletResponse response);
protected void doPost(HttpServletRequest request, HttpServletResponse response);
protected void doPut(HttpServletRequest request, HttpServletResponse response);
protected void doDelete(HttpServletRequest request, HttpServletResponse response);
protected void doOptions(HttpServletRequest request, HttpServletResponse response);
protected void doTrace(HttpServletRequest request, HttpServletResponse response);
}import org.eclipse.jetty.servlet.StatisticsServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.server.Server;
// Configure statistics servlet
ServletContextHandler context = new ServletContextHandler("/app");
// Add statistics servlet
ServletHolder statsServlet = new ServletHolder("statistics", StatisticsServlet.class);
context.addServlet(statsServlet, "/stats");
// Optional: Restrict access to statistics
FilterHolder securityFilter = new FilterHolder(AdminSecurityFilter.class);
context.addFilter(securityFilter, "/stats", EnumSet.of(DispatcherType.REQUEST));
// Add to server
Server server = new Server(8080);
server.setHandler(context);
server.start();
// Access statistics at: http://localhost:8080/app/statsimport org.eclipse.jetty.server.handler.StatisticsHandler;
import org.eclipse.jetty.servlet.ServletContextHandler;
// Add statistics handler to collect metrics
StatisticsHandler statsHandler = new StatisticsHandler();
ServletContextHandler context = new ServletContextHandler("/app");
// Wrap context with statistics handler
statsHandler.setHandler(context);
// Add application servlets
context.addServlet(MyApplicationServlet.class, "/api/*");
context.addServlet(StatisticsServlet.class, "/admin/stats");
// Configure server with statistics collection
Server server = new Server(8080);
server.setHandler(statsHandler);
server.start();
// Custom statistics servlet that exposes StatisticsHandler metrics
public class CustomStatisticsServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
// Find statistics handler in the handler hierarchy
StatisticsHandler statsHandler = findStatisticsHandler();
if (statsHandler == null) {
resp.sendError(503, "Statistics not available");
return;
}
// Generate statistics response
resp.setContentType("application/json");
PrintWriter out = resp.getWriter();
out.println("{");
out.println(" \"requests\": " + statsHandler.getRequests() + ",");
out.println(" \"requestsActive\": " + statsHandler.getRequestsActive() + ",");
out.println(" \"requestTimeMax\": " + statsHandler.getRequestTimeMax() + ",");
out.println(" \"requestTimeMean\": " + statsHandler.getRequestTimeMean() + ",");
out.println(" \"requestTimeStdDev\": " + statsHandler.getRequestTimeStdDev() + ",");
out.println(" \"responses1xx\": " + statsHandler.getResponses1xx() + ",");
out.println(" \"responses2xx\": " + statsHandler.getResponses2xx() + ",");
out.println(" \"responses3xx\": " + statsHandler.getResponses3xx() + ",");
out.println(" \"responses4xx\": " + statsHandler.getResponses4xx() + ",");
out.println(" \"responses5xx\": " + statsHandler.getResponses5xx() + ",");
out.println(" \"bytesReceived\": " + statsHandler.getBytesReceived() + ",");
out.println(" \"bytesSent\": " + statsHandler.getBytesSent() + ",");
out.println(" \"statsOnMs\": " + statsHandler.getStatsOnMs());
out.println("}");
}
private StatisticsHandler findStatisticsHandler() {
// Navigate handler hierarchy to find StatisticsHandler
ServletContext context = getServletContext();
ServletContextHandler contextHandler =
ServletContextHandler.getServletContextHandler(context);
Handler handler = contextHandler;
while (handler != null) {
if (handler instanceof StatisticsHandler) {
return (StatisticsHandler) handler;
}
if (handler instanceof HandlerWrapper) {
handler = ((HandlerWrapper) handler).getHandler();
} else {
break;
}
}
return null;
}
}import org.eclipse.jetty.jmx.MBeanContainer;
import org.eclipse.jetty.server.Server;
import javax.management.MBeanServer;
import java.lang.management.ManagementFactory;
// Enable JMX monitoring
public class JMXMonitoringSetup {
public static Server createServerWithJMX() throws Exception {
// Get platform MBean server
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
// Create MBean container
MBeanContainer mbeanContainer = new MBeanContainer(mbeanServer);
// Create server
Server server = new Server(8080);
// Add MBean container to server
server.addBean(mbeanContainer);
// Configure servlet context with JMX
ServletContextHandler context = new ServletContextHandler("/app");
// Add servlets and filters - they will be automatically exposed via JMX
ServletHolder dataServlet = new ServletHolder("dataServlet", DataServlet.class);
dataServlet.setDisplayName("Data Processing Servlet");
context.addServlet(dataServlet, "/data/*");
FilterHolder loggingFilter = new FilterHolder("loggingFilter", LoggingFilter.class);
loggingFilter.setDisplayName("Request Logging Filter");
context.addFilter(loggingFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
// Add statistics servlet for HTTP access to metrics
context.addServlet(StatisticsServlet.class, "/admin/stats");
server.setHandler(context);
return server;
}
}
// Custom MBean for application-specific metrics
public interface ApplicationMetricsMBean {
long getActiveUserCount();
long getTotalTransactions();
double getAverageResponseTime();
String getHealthStatus();
void resetCounters();
}
public class ApplicationMetrics implements ApplicationMetricsMBean {
private final AtomicLong activeUsers = new AtomicLong();
private final AtomicLong totalTransactions = new AtomicLong();
private final AtomicLong totalResponseTime = new AtomicLong();
private volatile String healthStatus = "HEALTHY";
@Override
public long getActiveUserCount() {
return activeUsers.get();
}
@Override
public long getTotalTransactions() {
return totalTransactions.get();
}
@Override
public double getAverageResponseTime() {
long transactions = totalTransactions.get();
return transactions > 0 ? (double) totalResponseTime.get() / transactions : 0.0;
}
@Override
public String getHealthStatus() {
return healthStatus;
}
@Override
public void resetCounters() {
totalTransactions.set(0);
totalResponseTime.set(0);
// Don't reset active users as it's current state
}
// Methods for internal use
public void incrementActiveUsers() {
activeUsers.incrementAndGet();
}
public void decrementActiveUsers() {
activeUsers.decrementAndGet();
}
public void recordTransaction(long responseTimeMs) {
totalTransactions.incrementAndGet();
totalResponseTime.addAndGet(responseTimeMs);
}
public void setHealthStatus(String status) {
this.healthStatus = status;
}
}
// Register custom MBean
public class ApplicationMetricsSetup {
public static void registerApplicationMetrics(Server server, ApplicationMetrics metrics)
throws Exception {
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName objectName = new ObjectName("org.eclipse.jetty.servlet:type=ApplicationMetrics");
mbeanServer.registerMBean(metrics, objectName);
// Make metrics available to servlets via servlet context
ServletContextHandler context = (ServletContextHandler) server.getHandler();
context.getServletContext().setAttribute("applicationMetrics", metrics);
}
}import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class PerformanceMonitoringFilter implements Filter {
private ApplicationMetrics metrics;
@Override
public void init(FilterConfig filterConfig) {
ServletContext context = filterConfig.getServletContext();
metrics = (ApplicationMetrics) context.getAttribute("applicationMetrics");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest) request;
HttpServletResponse httpResp = (HttpServletResponse) response;
long startTime = System.currentTimeMillis();
// Track active users (sessions)
HttpSession session = httpReq.getSession(false);
boolean newSession = false;
if (session == null) {
session = httpReq.getSession(true);
newSession = true;
if (metrics != null) {
metrics.incrementActiveUsers();
}
}
try {
chain.doFilter(request, response);
} finally {
long responseTime = System.currentTimeMillis() - startTime;
// Record performance metrics
if (metrics != null) {
metrics.recordTransaction(responseTime);
// Check for error conditions
int status = httpResp.getStatus();
if (status >= 500) {
metrics.setHealthStatus("DEGRADED");
}
}
// Log slow requests
if (responseTime > 5000) { // 5 seconds
System.err.println("SLOW REQUEST: " + httpReq.getRequestURI() +
" took " + responseTime + "ms");
}
}
}
@Override
public void destroy() {
// Cleanup
}
}
// Session listener to track user sessions
public class UserSessionListener implements HttpSessionListener {
private ApplicationMetrics metrics;
public UserSessionListener(ApplicationMetrics metrics) {
this.metrics = metrics;
}
@Override
public void sessionCreated(HttpSessionEvent se) {
// Already tracked in filter when session is created
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
if (metrics != null) {
metrics.decrementActiveUsers();
}
}
}public class HealthCheckServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
ServletContext context = getServletContext();
ApplicationMetrics metrics = (ApplicationMetrics) context.getAttribute("applicationMetrics");
// Perform health checks
HealthStatus status = performHealthChecks();
// Set response status based on health
if (status.isHealthy()) {
resp.setStatus(200);
} else {
resp.setStatus(503); // Service Unavailable
}
// Generate health report
resp.setContentType("application/json");
PrintWriter out = resp.getWriter();
out.println("{");
out.println(" \"status\": \"" + status.getOverallStatus() + "\",");
out.println(" \"timestamp\": " + System.currentTimeMillis() + ",");
if (metrics != null) {
out.println(" \"activeUsers\": " + metrics.getActiveUserCount() + ",");
out.println(" \"totalTransactions\": " + metrics.getTotalTransactions() + ",");
out.println(" \"averageResponseTime\": " + metrics.getAverageResponseTime() + ",");
out.println(" \"applicationStatus\": \"" + metrics.getHealthStatus() + "\",");
}
out.println(" \"checks\": {");
Map<String, Boolean> checks = status.getChecks();
boolean first = true;
for (Map.Entry<String, Boolean> check : checks.entrySet()) {
if (!first) out.println(",");
out.println(" \"" + check.getKey() + "\": " + check.getValue());
first = false;
}
out.println(" }");
out.println("}");
}
private HealthStatus performHealthChecks() {
Map<String, Boolean> checks = new HashMap<>();
// Database connectivity check
checks.put("database", checkDatabaseConnection());
// External service checks
checks.put("externalAPI", checkExternalService());
// File system checks
checks.put("fileSystem", checkFileSystemAccess());
// Memory check
checks.put("memory", checkMemoryUsage());
// Determine overall status
boolean allHealthy = checks.values().stream().allMatch(Boolean::booleanValue);
String overallStatus = allHealthy ? "HEALTHY" : "UNHEALTHY";
return new HealthStatus(overallStatus, checks);
}
private boolean checkDatabaseConnection() {
try {
// Attempt database connection
// Return true if successful, false otherwise
return true; // Placeholder
} catch (Exception e) {
return false;
}
}
private boolean checkExternalService() {
try {
// Check external service availability
// Return true if reachable, false otherwise
return true; // Placeholder
} catch (Exception e) {
return false;
}
}
private boolean checkFileSystemAccess() {
try {
// Check file system read/write access
File tempFile = File.createTempFile("health", ".check");
boolean canWrite = tempFile.exists();
tempFile.delete();
return canWrite;
} catch (Exception e) {
return false;
}
}
private boolean checkMemoryUsage() {
Runtime runtime = Runtime.getRuntime();
long maxMemory = runtime.maxMemory();
long totalMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
long usedMemory = totalMemory - freeMemory;
// Consider unhealthy if using more than 90% of max memory
double memoryUsage = (double) usedMemory / maxMemory;
return memoryUsage < 0.90;
}
// Helper class for health status
private static class HealthStatus {
private final String overallStatus;
private final Map<String, Boolean> checks;
public HealthStatus(String overallStatus, Map<String, Boolean> checks) {
this.overallStatus = overallStatus;
this.checks = new HashMap<>(checks);
}
public boolean isHealthy() {
return "HEALTHY".equals(overallStatus);
}
public String getOverallStatus() {
return overallStatus;
}
public Map<String, Boolean> getChecks() {
return checks;
}
}
}// Complete monitoring and statistics setup
public class MonitoringConfiguration {
public static Server createMonitoredServer() throws Exception {
// Enable JMX
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
MBeanContainer mbeanContainer = new MBeanContainer(mbeanServer);
// Create server with statistics
Server server = new Server(8080);
server.addBean(mbeanContainer);
// Add statistics handler
StatisticsHandler statsHandler = new StatisticsHandler();
// Create application context
ServletContextHandler context = new ServletContextHandler("/app");
// Create and register application metrics
ApplicationMetrics appMetrics = new ApplicationMetrics();
registerApplicationMetrics(server, appMetrics);
context.getServletContext().setAttribute("applicationMetrics", appMetrics);
// Add performance monitoring filter
FilterHolder perfFilter = new FilterHolder(PerformanceMonitoringFilter.class);
context.addFilter(perfFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
// Add session listener
context.addEventListener(new UserSessionListener(appMetrics));
// Add application servlets
context.addServlet(MyApplicationServlet.class, "/api/*");
// Add monitoring endpoints
addMonitoringEndpoints(context);
// Wire handlers together
statsHandler.setHandler(context);
server.setHandler(statsHandler);
return server;
}
private static void addMonitoringEndpoints(ServletContextHandler context) {
// Statistics servlet (built-in Jetty statistics)
context.addServlet(StatisticsServlet.class, "/admin/stats");
// Custom statistics servlet (application + server metrics)
context.addServlet(CustomStatisticsServlet.class, "/admin/metrics");
// Health check endpoint
context.addServlet(HealthCheckServlet.class, "/admin/health");
// JMX servlet (if needed for HTTP access to JMX)
context.addServlet(JMXServlet.class, "/admin/jmx");
// Restrict access to admin endpoints
FilterHolder adminFilter = new FilterHolder(AdminSecurityFilter.class);
context.addFilter(adminFilter, "/admin/*", EnumSet.of(DispatcherType.REQUEST));
}
private static void registerApplicationMetrics(Server server, ApplicationMetrics metrics)
throws Exception {
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName objectName = new ObjectName("org.eclipse.jetty.servlet:type=ApplicationMetrics");
mbeanServer.registerMBean(metrics, objectName);
}
}
// Usage
public class MonitoredApplication {
public static void main(String[] args) throws Exception {
Server server = MonitoringConfiguration.createMonitoredServer();
server.start();
System.out.println("Server started with monitoring:");
System.out.println(" Application: http://localhost:8080/app/");
System.out.println(" Statistics: http://localhost:8080/app/admin/stats");
System.out.println(" Metrics: http://localhost:8080/app/admin/metrics");
System.out.println(" Health: http://localhost:8080/app/admin/health");
System.out.println(" JMX: Available via JConsole or similar tools");
server.join();
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-eclipse-jetty--jetty-servlet