Eclipse Jetty servlet container providing comprehensive servlet, filter, and listener integration with lifecycle management and dynamic registration support.
—
Filter handling in Eclipse Jetty provides comprehensive request/response filtering capabilities with support for different dispatcher types, URL pattern matching, and lifecycle management through the FilterHolder and FilterMapping classes.
The FilterHolder manages individual filter instances, their configuration, and lifecycle.
public class FilterHolder extends Holder<Filter> {
// Constructors
public FilterHolder();
public FilterHolder(Source source);
public FilterHolder(Class<? extends Filter> filter);
public FilterHolder(Filter filter);
// Filter instance management
public void setFilter(Filter filter);
public Filter getFilter();
// Lifecycle management
public void doStart();
public void initialize();
public void doStop();
public void destroyInstance(Object instance);
// Registration access
public FilterRegistration.Dynamic getRegistration();
// String representation
public String toString();
}The FilterMapping class defines how filters are applied to requests based on URL patterns, servlet names, and dispatcher types.
public class FilterMapping implements Dumpable {
// Dispatcher type constants
public static final int DEFAULT = 0;
public static final int REQUEST = 1;
public static final int FORWARD = 2;
public static final int INCLUDE = 4;
public static final int ERROR = 8;
public static final int ASYNC = 16;
public static final int ALL = 31;
// Constructor
public FilterMapping();
// Filter identification
public String getFilterName();
public void setFilterName(String filterName);
// URL pattern mapping
public String[] getPathSpecs();
public void setPathSpecs(String[] pathSpecs);
public void setPathSpec(String pathSpec);
// Servlet name mapping
public String[] getServletNames();
public void setServletNames(String[] servletNames);
public void setServletName(String servletName);
// Dispatcher type configuration
public EnumSet<DispatcherType> getDispatcherTypes();
public void setDispatcherTypes(EnumSet<DispatcherType> dispatcherTypes);
public void setDispatches(int dispatches);
public int getDispatches();
// Mapping properties
public boolean isDefaultMapping();
public boolean isMatchAfter();
public void setMatchAfter(boolean matchAfter);
// Utility methods
public String toString();
public void dump(Appendable out, String indent);
}import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.FilterMapping;
import org.eclipse.jetty.servlet.ServletHandler;
import jakarta.servlet.Filter;
import jakarta.servlet.DispatcherType;
import java.util.EnumSet;
// Create servlet handler
ServletHandler handler = new ServletHandler();
// Method 1: Add filter with mapping in one call
FilterHolder holder1 = handler.addFilterWithMapping(
LoggingFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
// Method 2: Add filter and mapping separately
FilterHolder holder2 = new FilterHolder("authFilter", AuthenticationFilter.class);
handler.addFilter(holder2);
FilterMapping mapping = new FilterMapping();
mapping.setFilterName("authFilter");
mapping.setPathSpec("/secure/*");
mapping.setDispatcherTypes(EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD));
handler.addFilterMapping(mapping);
// Method 3: Using class name string
FilterHolder holder3 = handler.addFilterWithMapping(
"com.example.CacheFilter", "/api/*", EnumSet.of(DispatcherType.REQUEST));import org.eclipse.jetty.servlet.FilterHolder;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
// Create and configure filter holder
FilterHolder filterHolder = new FilterHolder("compressionFilter", CompressionFilter.class);
// Set initialization parameters
filterHolder.setInitParameter("threshold", "1024");
filterHolder.setInitParameter("mimeTypes", "text/html,text/css,application/javascript");
filterHolder.setInitParameter("enabled", "true");
// Set display name for management
filterHolder.setDisplayName("Content Compression Filter");
// Enable async support
filterHolder.setAsyncSupported(true);
// Add to handler with specific mapping
handler.addFilter(filterHolder);
// Create detailed mapping
FilterMapping mapping = new FilterMapping();
mapping.setFilterName("compressionFilter");
mapping.setPathSpecs(new String[]{"*.html", "*.css", "*.js", "/api/*"});
mapping.setDispatcherTypes(EnumSet.of(
DispatcherType.REQUEST,
DispatcherType.ASYNC
));
handler.addFilterMapping(mapping);import jakarta.servlet.DispatcherType;
import java.util.EnumSet;
// Security filter for all request types
FilterHolder securityFilter = new FilterHolder("security", SecurityFilter.class);
FilterMapping securityMapping = new FilterMapping();
securityMapping.setFilterName("security");
securityMapping.setPathSpec("/secure/*");
securityMapping.setDispatcherTypes(EnumSet.of(
DispatcherType.REQUEST, // Normal requests
DispatcherType.FORWARD, // RequestDispatcher.forward()
DispatcherType.INCLUDE, // RequestDispatcher.include()
DispatcherType.ERROR, // Error page requests
DispatcherType.ASYNC // Async requests
));
// Logging filter for requests only
FilterHolder loggingFilter = new FilterHolder("logging", LoggingFilter.class);
FilterMapping loggingMapping = new FilterMapping();
loggingMapping.setFilterName("logging");
loggingMapping.setPathSpec("/*");
loggingMapping.setDispatcherTypes(EnumSet.of(DispatcherType.REQUEST));
// Cache filter for requests and forwards
FilterHolder cacheFilter = new FilterHolder("cache", CacheFilter.class);
FilterMapping cacheMapping = new FilterMapping();
cacheMapping.setFilterName("cache");
cacheMapping.setPathSpec("/static/*");
cacheMapping.setDispatcherTypes(EnumSet.of(
DispatcherType.REQUEST,
DispatcherType.FORWARD
));
// Add filters to handler
handler.addFilter(securityFilter);
handler.addFilterMapping(securityMapping);
handler.addFilter(loggingFilter);
handler.addFilterMapping(loggingMapping);
handler.addFilter(cacheFilter);
handler.addFilterMapping(cacheMapping);// Filter that handles multiple URL patterns
FilterHolder multiPatternFilter = new FilterHolder("multiPattern", MultiPatternFilter.class);
FilterMapping multiMapping = new FilterMapping();
multiMapping.setFilterName("multiPattern");
multiMapping.setPathSpecs(new String[]{
"/api/v1/*", // API version 1
"/api/v2/*", // API version 2
"*.json", // All JSON requests
"/admin/*", // Admin interface
"/reports/*" // Reports section
});
multiMapping.setDispatcherTypes(EnumSet.of(DispatcherType.REQUEST));
handler.addFilter(multiPatternFilter);
handler.addFilterMapping(multiMapping);// Filter that applies to specific servlets by name
FilterHolder servletSpecificFilter = new FilterHolder("servletFilter", ServletSpecificFilter.class);
FilterMapping servletMapping = new FilterMapping();
servletMapping.setFilterName("servletFilter");
servletMapping.setServletNames(new String[]{
"dataServlet", // Apply to data servlet
"reportServlet", // Apply to report servlet
"adminServlet" // Apply to admin servlet
});
servletMapping.setDispatcherTypes(EnumSet.of(DispatcherType.REQUEST));
handler.addFilter(servletSpecificFilter);
handler.addFilterMapping(servletMapping);
// Can also set single servlet name
FilterMapping singleServletMapping = new FilterMapping();
singleServletMapping.setFilterName("servletFilter");
singleServletMapping.setServletName("criticalServlet");import org.eclipse.jetty.servlet.ServletHandler;
// Create handler with filter chain configuration
ServletHandler handler = new ServletHandler();
// Enable filter chain caching for performance
handler.setFilterChainsCached(true);
handler.setMaxFilterChainsCacheSize(1000);
// Security filter - should run first
FilterHolder securityFilter = new FilterHolder("security", SecurityFilter.class);
FilterMapping securityMapping = new FilterMapping();
securityMapping.setFilterName("security");
securityMapping.setPathSpec("/*");
securityMapping.setDispatcherTypes(EnumSet.of(DispatcherType.REQUEST));
// Logging filter - should run after security
FilterHolder loggingFilter = new FilterHolder("logging", LoggingFilter.class);
FilterMapping loggingMapping = new FilterMapping();
loggingMapping.setFilterName("logging");
loggingMapping.setPathSpec("/*");
loggingMapping.setDispatcherTypes(EnumSet.of(DispatcherType.REQUEST));
// Compression filter - should run last
FilterHolder compressionFilter = new FilterHolder("compression", CompressionFilter.class);
FilterMapping compressionMapping = new FilterMapping();
compressionMapping.setFilterName("compression");
compressionMapping.setPathSpec("/*");
compressionMapping.setDispatcherTypes(EnumSet.of(DispatcherType.REQUEST));
// Add filters in order (security -> logging -> compression)
handler.addFilter(securityFilter);
handler.addFilterMapping(securityMapping);
handler.addFilter(loggingFilter);
handler.addFilterMapping(loggingMapping);
handler.addFilter(compressionFilter);
handler.addFilterMapping(compressionMapping);
// Alternative: Use prepend to insert at beginning of chain
FilterMapping emergencyMapping = new FilterMapping();
emergencyMapping.setFilterName("emergency");
emergencyMapping.setPathSpec("/emergency/*");
handler.prependFilterMapping(emergencyMapping);// Create complex filter with conditional matching
FilterHolder conditionalFilter = new FilterHolder("conditional", ConditionalFilter.class);
conditionalFilter.setInitParameter("enabledHours", "09:00-17:00");
conditionalFilter.setInitParameter("enabledDays", "MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY");
FilterMapping conditionalMapping = new FilterMapping();
conditionalMapping.setFilterName("conditional");
conditionalMapping.setPathSpec("/business/*");
conditionalMapping.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
// Set to match after servlet matching (rare use case)
conditionalMapping.setMatchAfter(true);
handler.addFilter(conditionalFilter);
handler.addFilterMapping(conditionalMapping);
// Check if mapping is default mapping
if (conditionalMapping.isDefaultMapping()) {
System.out.println("This is a default mapping");
}
// Check match after setting
if (conditionalMapping.isMatchAfter()) {
System.out.println("Filter matches after servlet matching");
}public class LifecycleAwareFilter implements Filter {
private FilterConfig config;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.config = filterConfig;
System.out.println("Filter initialized: " + filterConfig.getFilterName());
// Access initialization parameters
Enumeration<String> paramNames = filterConfig.getInitParameterNames();
while (paramNames.hasMoreElements()) {
String name = paramNames.nextElement();
String value = filterConfig.getInitParameter(name);
System.out.println(" " + name + " = " + value);
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// Pre-processing
long startTime = System.currentTimeMillis();
try {
// Continue filter chain
chain.doFilter(request, response);
} finally {
// Post-processing
long duration = System.currentTimeMillis() - startTime;
System.out.println("Request processed in " + duration + "ms");
}
}
@Override
public void destroy() {
System.out.println("Filter destroyed: " + config.getFilterName());
config = null;
}
}
// Configure lifecycle-aware filter
FilterHolder lifecycleFilter = new FilterHolder("lifecycle", LifecycleAwareFilter.class);
lifecycleFilter.setInitParameter("debug", "true");
// Start filter (calls init())
try {
lifecycleFilter.start();
} catch (Exception e) {
System.err.println("Failed to start filter: " + e.getMessage());
}
// Stop filter (calls destroy())
try {
lifecycleFilter.stop();
} catch (Exception e) {
System.err.println("Failed to stop filter: " + e.getMessage());
}import jakarta.servlet.FilterRegistration;
// Create filter holder and get dynamic registration
FilterHolder dynamicFilter = new FilterHolder("dynamic", DynamicFilter.class);
handler.addFilter(dynamicFilter);
// Access filter registration for dynamic configuration
FilterRegistration.Dynamic registration = dynamicFilter.getRegistration();
if (registration != null) {
// Set initialization parameters dynamically
registration.setInitParameter("runtime.config", "dynamic-value");
// Set async support
registration.setAsyncSupported(true);
// Add URL patterns dynamically
registration.addMappingForUrlPatterns(
EnumSet.of(DispatcherType.REQUEST),
true, // isMatchAfter
"/dynamic/*", "*.dyn"
);
// Add servlet name mappings
registration.addMappingForServletNames(
EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD),
true, // isMatchAfter
"dynamicServlet", "testServlet"
);
}public class ErrorHandlingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(request, response);
} catch (ServletException | IOException e) {
// Log error
System.err.println("Error in filter chain: " + e.getMessage());
// Set error attributes for error page
request.setAttribute("jakarta.servlet.error.exception", e);
request.setAttribute("jakarta.servlet.error.exception_type", e.getClass());
request.setAttribute("jakarta.servlet.error.message", e.getMessage());
// Forward to error page or re-throw
if (response instanceof HttpServletResponse) {
((HttpServletResponse) response).sendError(500, "Internal Server Error");
} else {
throw e;
}
}
}
}
// Configure error handling filter to run on ERROR dispatch type
FilterHolder errorFilter = new FilterHolder("errorHandler", ErrorHandlingFilter.class);
FilterMapping errorMapping = new FilterMapping();
errorMapping.setFilterName("errorHandler");
errorMapping.setPathSpec("/*");
errorMapping.setDispatcherTypes(EnumSet.of(
DispatcherType.REQUEST,
DispatcherType.ERROR
));
handler.addFilter(errorFilter);
handler.addFilterMapping(errorMapping);Install with Tessl CLI
npx tessl i tessl/maven-org-eclipse-jetty--jetty-servlet