Collection of utility servlets and filters for Jakarta EE 10 web applications including CORS, DoS protection, QoS management, header manipulation, and server-sent events.
—
Abstract base class for filters that need path, MIME type, or HTTP method-based filtering capabilities. The IncludeExcludeBasedFilter provides a foundation for creating custom filters with flexible request matching criteria.
Abstract base class providing common filtering patterns for servlet filters.
/**
* Abstract base filter for include/exclude filtering based on paths, MIME types, and HTTP methods.
* Provides common functionality for filters that need to selectively process requests.
*/
public abstract class IncludeExcludeBasedFilter implements Filter {
/**
* Initialize the filter with include/exclude configuration parameters
* @param filterConfig Filter configuration containing include/exclude parameters
* @throws ServletException if configuration is invalid
*/
public void init(FilterConfig filterConfig) throws ServletException;
/**
* Clean up filter resources
*/
public void destroy();
/**
* Get string representation of the filter configuration
* @return String describing the include/exclude patterns
*/
public String toString();
/**
* Determine if the request should be filtered based on configured patterns
* @param httpRequest The HTTP request
* @param httpResponse The HTTP response
* @return true if the request should be processed by this filter
*/
protected boolean shouldFilter(HttpServletRequest httpRequest, HttpServletResponse httpResponse);
/**
* Guess the MIME type of the response for filtering decisions
* @param httpRequest The HTTP request
* @param httpResponse The HTTP response
* @return The guessed MIME type or null if cannot be determined
*/
protected String guessMimeType(HttpServletRequest httpRequest, HttpServletResponse httpResponse);
}includedPaths: Comma-separated list of path specifications to include excludedPaths: Comma-separated list of path specifications to exclude
Path specifications support three formats:
^ (e.g., ^/api/v\d+/.*)/ for exact or prefix matching (e.g., /admin/*, /api/users)*. for extension matching (e.g., *.jsp, *.html)includedMimeTypes: Comma-separated list of MIME types to include excludedMimeTypes: Comma-separated list of MIME types to exclude
MIME type patterns support:
text/html, application/jsontext/*, image/*, application/*includedHttpMethods: Comma-separated list of HTTP methods to include excludedHttpMethods: Comma-separated list of HTTP methods to exclude
Standard HTTP methods: GET, POST, PUT, DELETE, HEAD, OPTIONS, PATCH, TRACE
import org.eclipse.jetty.ee10.servlets.IncludeExcludeBasedFilter;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* Custom security filter that adds security headers to specific paths
*/
public class SecurityHeaderFilter extends IncludeExcludeBasedFilter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// Check if this request should be filtered
if (shouldFilter(httpRequest, httpResponse)) {
// Add security headers
httpResponse.setHeader("X-Frame-Options", "DENY");
httpResponse.setHeader("X-Content-Type-Options", "nosniff");
httpResponse.setHeader("X-XSS-Protection", "1; mode=block");
}
// Continue with the filter chain
chain.doFilter(request, response);
}
}Configuration:
<filter>
<filter-name>SecurityHeaderFilter</filter-name>
<filter-class>com.example.SecurityHeaderFilter</filter-class>
<!-- Only apply to HTML pages -->
<init-param>
<param-name>includedMimeTypes</param-name>
<param-value>text/html</param-value>
</init-param>
<!-- Skip admin pages (they have their own security) -->
<init-param>
<param-name>excludedPaths</param-name>
<param-value>/admin/*</param-value>
</init-param>
</filter>/**
* Logging filter that logs specific types of requests
*/
public class ApiLogFilter extends IncludeExcludeBasedFilter {
private static final Logger logger = LoggerFactory.getLogger(ApiLogFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
long startTime = System.currentTimeMillis();
try {
chain.doFilter(request, response);
} finally {
// Log if this request should be filtered
if (shouldFilter(httpRequest, httpResponse)) {
long duration = System.currentTimeMillis() - startTime;
logger.info("API Request: {} {} - Status: {} - Duration: {}ms",
httpRequest.getMethod(),
httpRequest.getRequestURI(),
httpResponse.getStatus(),
duration);
}
}
}
}Configuration:
<filter>
<filter-name>ApiLogFilter</filter-name>
<filter-class>com.example.ApiLogFilter</filter-class>
<!-- Only log API endpoints -->
<init-param>
<param-name>includedPaths</param-name>
<param-value>/api/*</param-value>
</init-param>
<!-- Only log POST, PUT, DELETE (not GET) -->
<init-param>
<param-name>includedHttpMethods</param-name>
<param-value>POST,PUT,DELETE</param-value>
</init-param>
</filter>/**
* Simple compression filter that compresses specific content types
*/
public class CompressionFilter extends IncludeExcludeBasedFilter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// Check if client accepts gzip
String acceptEncoding = httpRequest.getHeader("Accept-Encoding");
boolean clientSupportsGzip = acceptEncoding != null &&
acceptEncoding.toLowerCase().contains("gzip");
if (clientSupportsGzip && shouldFilter(httpRequest, httpResponse)) {
// Wrap response with compression
GzipResponseWrapper wrappedResponse = new GzipResponseWrapper(httpResponse);
chain.doFilter(request, wrappedResponse);
wrappedResponse.finish();
} else {
chain.doFilter(request, response);
}
}
}Configuration:
<filter>
<filter-name>CompressionFilter</filter-name>
<filter-class>com.example.CompressionFilter</filter-class>
<!-- Compress text content -->
<init-param>
<param-name>includedMimeTypes</param-name>
<param-value>text/html,text/css,text/javascript,application/json,application/xml</param-value>
</init-param>
<!-- Don't compress images -->
<init-param>
<param-name>excludedMimeTypes</param-name>
<param-value>image/*</param-value>
</init-param>
<!-- Only compress larger files -->
<init-param>
<param-name>excludedPaths</param-name>
<param-value>*.ico,/favicon.ico</param-value>
</init-param>
</filter>/**
* Authentication filter that checks tokens for specific endpoints
*/
public class TokenAuthFilter extends IncludeExcludeBasedFilter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
if (shouldFilter(httpRequest, httpResponse)) {
String authHeader = httpRequest.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED,
"Missing or invalid authorization header");
return;
}
String token = authHeader.substring(7);
if (!validateToken(token)) {
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED,
"Invalid token");
return;
}
}
chain.doFilter(request, response);
}
private boolean validateToken(String token) {
// Implementation-specific token validation
return TokenService.isValid(token);
}
}Configuration:
<filter>
<filter-name>TokenAuthFilter</filter-name>
<filter-class>com.example.TokenAuthFilter</filter-class>
<!-- Only protect API endpoints -->
<init-param>
<param-name>includedPaths</param-name>
<param-value>/api/*</param-value>
</init-param>
<!-- Skip authentication endpoints -->
<init-param>
<param-name>excludedPaths</param-name>
<param-value>/api/auth/*,/api/public/*</param-value>
</init-param>
<!-- Only for operations that modify data -->
<init-param>
<param-name>includedHttpMethods</param-name>
<param-value>POST,PUT,DELETE,PATCH</param-value>
</init-param>
</filter>/admin/usersMatches only the exact path /admin/users.
/api/*Matches any path starting with /api/ including:
/api/users/api/products/123/api/v1/search*.jsp
*.html
*.cssMatches any path ending with the specified extension.
^/user/\d+$
^/api/v\d+/.*Matches paths using regular expression patterns. Must start with ^.
text/html
application/json
application/pdftext/* # All text types
image/* # All image types
application/* # All application typestext/html,text/css,text/javascript<filter>
<filter-name>ComplexPathFilter</filter-name>
<filter-class>com.example.MyFilter</filter-class>
<!-- Include API and admin paths -->
<init-param>
<param-name>includedPaths</param-name>
<param-value>/api/*,/admin/*</param-value>
</init-param>
<!-- Exclude specific admin and API paths -->
<init-param>
<param-name>excludedPaths</param-name>
<param-value>/admin/login,/api/health,/api/public/*</param-value>
</init-param>
</filter><filter>
<filter-name>ContentTypeFilter</filter-name>
<filter-class>com.example.MyFilter</filter-class>
<!-- Include JSON and XML responses -->
<init-param>
<param-name>includedMimeTypes</param-name>
<param-value>application/json,application/xml,text/xml</param-value>
</init-param>
<!-- Exclude binary content -->
<init-param>
<param-name>excludedMimeTypes</param-name>
<param-value>image/*,video/*,application/octet-stream</param-value>
</init-param>
</filter><filter>
<filter-name>WriteOperationFilter</filter-name>
<filter-class>com.example.MyFilter</filter-class>
<!-- Only filter write operations -->
<init-param>
<param-name>includedHttpMethods</param-name>
<param-value>POST,PUT,DELETE,PATCH</param-value>
</init-param>
<!-- Skip OPTIONS (preflight) -->
<init-param>
<param-name>excludedHttpMethods</param-name>
<param-value>OPTIONS</param-value>
</init-param>
</filter><filter>
<filter-name>CombinedFilter</filter-name>
<filter-class>com.example.MyFilter</filter-class>
<!-- Filter API endpoints -->
<init-param>
<param-name>includedPaths</param-name>
<param-value>/api/*</param-value>
</init-param>
<!-- Only JSON responses -->
<init-param>
<param-name>includedMimeTypes</param-name>
<param-value>application/json</param-value>
</init-param>
<!-- Only write operations -->
<init-param>
<param-name>includedHttpMethods</param-name>
<param-value>POST,PUT,DELETE</param-value>
</init-param>
<!-- Skip health checks -->
<init-param>
<param-name>excludedPaths</param-name>
<param-value>/api/health,/api/status</param-value>
</init-param>
</filter>@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// Check if this request should be processed
if (shouldFilter(httpRequest, httpResponse)) {
// Apply your filter logic here
processRequest(httpRequest, httpResponse);
}
// Always continue the chain
chain.doFilter(request, response);
}The base class provides MIME type detection through:
Apply transformations only to specific content types (HTML, JSON, XML).
Add security headers or authentication checks to sensitive paths.
Log or monitor specific API endpoints or operations.
Compress responses for text-based content types.
Add cache control headers to static resources or API responses.
Install with Tessl CLI
npx tessl i tessl/maven-org-eclipse-jetty-ee10--jetty-ee10-servlets