Eclipse Jetty servlet container providing comprehensive servlet, filter, and listener integration with lifecycle management and dynamic registration support.
—
Eclipse Jetty's servlet testing utilities provide comprehensive support for testing servlets, filters, and web applications without requiring a full server deployment through the ServletTester class and related testing infrastructure.
Note: ServletTester may be deprecated and eventually removed in future Jetty versions. For new projects, consider using the combination of
Server+LocalConnector+ServletContextHandlerfor servlet testing instead.
The ServletTester provides a lightweight testing environment for servlet-based applications.
public class ServletTester extends ContainerLifeCycle {
// Constructors
public ServletTester();
public ServletTester(String contextPath);
public ServletTester(String contextPath, int options);
// Context management
public ServletContextHandler getContext();
public void setContextPath(String contextPath);
public String getContextPath();
// Servlet registration
public void addServlet(Class<? extends Servlet> servlet, String pathSpec);
public ServletHolder addServlet(String className, String pathSpec);
// Filter registration
public void addFilter(Class<? extends Filter> filter, String pathSpec,
EnumSet<DispatcherType> dispatches);
public FilterHolder addFilter(String className, String pathSpec,
EnumSet<DispatcherType> dispatches);
// Request execution
public String getResponses(String request);
public String getResponses(String request, long idleFor, TimeUnit units);
}import org.eclipse.jetty.servlet.ServletTester;
import org.eclipse.jetty.servlet.ServletHolder;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
// Test servlet implementation
public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
resp.setContentType("text/plain");
resp.getWriter().println("Hello from test servlet!");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
String data = req.getParameter("data");
resp.setContentType("application/json");
resp.getWriter().println("{\"received\":\"" + data + "\"}");
}
}
// Basic servlet test
public class BasicServletTest {
@Test
public void testSimpleGetRequest() throws Exception {
// Create servlet tester
ServletTester tester = new ServletTester();
tester.setContextPath("/test");
// Add servlet
tester.addServlet(TestServlet.class, "/hello");
// Start tester
tester.start();
try {
// Create HTTP request
String request = "GET /test/hello HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Connection: close\r\n" +
"\r\n";
// Execute request and get response
String response = tester.getResponses(request);
// Verify response
assertTrue(response.contains("HTTP/1.1 200 OK"));
assertTrue(response.contains("Hello from test servlet!"));
} finally {
tester.stop();
}
}
@Test
public void testPostRequest() throws Exception {
ServletTester tester = new ServletTester("/api");
tester.addServlet(TestServlet.class, "/data");
tester.start();
try {
String requestBody = "data=testvalue";
String request = "POST /api/data HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Content-Type: application/x-www-form-urlencoded\r\n" +
"Content-Length: " + requestBody.length() + "\r\n" +
"Connection: close\r\n" +
"\r\n" +
requestBody;
String response = tester.getResponses(request);
assertTrue(response.contains("HTTP/1.1 200 OK"));
assertTrue(response.contains("{\"received\":\"testvalue\"}"));
} finally {
tester.stop();
}
}
}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;
import jakarta.servlet.DispatcherType;
import java.util.EnumSet;
// Test filter implementation
public class LoggingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
// Initialize filter
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest) request;
HttpServletResponse httpResp = (HttpServletResponse) response;
// Add request ID header
String requestId = "REQ-" + System.currentTimeMillis();
httpResp.setHeader("X-Request-ID", requestId);
// Log request
System.out.println("Processing request: " + httpReq.getRequestURI() +
" [" + requestId + "]");
// Continue chain
chain.doFilter(request, response);
// Log response
System.out.println("Response status: " + httpResp.getStatus() +
" [" + requestId + "]");
}
@Override
public void destroy() {
// Cleanup filter
}
}
// Filter test
public class FilterTest {
@Test
public void testFilterChain() throws Exception {
ServletTester tester = new ServletTester();
// Add filter
tester.addFilter(LoggingFilter.class, "/*",
EnumSet.of(DispatcherType.REQUEST));
// Add servlet
tester.addServlet(TestServlet.class, "/test");
tester.start();
try {
String request = "GET /test HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Connection: close\r\n" +
"\r\n";
String response = tester.getResponses(request);
// Verify filter added header
assertTrue(response.contains("X-Request-ID: REQ-"));
assertTrue(response.contains("Hello from test servlet!"));
} finally {
tester.stop();
}
}
}// Complex servlet with dependencies
public class DataServlet extends HttpServlet {
private DatabaseService dbService;
@Override
public void init() throws ServletException {
super.init();
// Initialize database service (mock for testing)
String dbUrl = getInitParameter("database.url");
this.dbService = new MockDatabaseService(dbUrl);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
String id = req.getParameter("id");
if (id == null) {
resp.sendError(400, "Missing id parameter");
return;
}
try {
String data = dbService.getData(id);
if (data == null) {
resp.sendError(404, "Data not found");
return;
}
resp.setContentType("application/json");
resp.getWriter().println("{\"id\":\"" + id + "\",\"data\":\"" + data + "\"}");
} catch (Exception e) {
resp.sendError(500, "Database error: " + e.getMessage());
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
String data = req.getParameter("data");
if (data == null || data.trim().isEmpty()) {
resp.sendError(400, "Missing data parameter");
return;
}
try {
String id = dbService.saveData(data);
resp.setContentType("application/json");
resp.getWriter().println("{\"id\":\"" + id + "\",\"status\":\"saved\"}");
} catch (Exception e) {
resp.sendError(500, "Failed to save data: " + e.getMessage());
}
}
@Override
public void destroy() {
if (dbService != null) {
dbService.close();
}
super.destroy();
}
}
// Mock database service for testing
public class MockDatabaseService {
private final Map<String, String> data = new HashMap<>();
private int nextId = 1;
public MockDatabaseService(String url) {
// Initialize with test data
data.put("1", "Test data 1");
data.put("2", "Test data 2");
}
public String getData(String id) {
return data.get(id);
}
public String saveData(String value) {
String id = String.valueOf(nextId++);
data.put(id, value);
return id;
}
public void close() {
data.clear();
}
}
// Complex application test
public class ComplexApplicationTest {
@Test
public void testDataRetrieval() throws Exception {
ServletTester tester = new ServletTester("/api");
// Configure servlet with init parameters
ServletHolder dataServlet = tester.addServlet(DataServlet.class.getName(), "/data");
dataServlet.setInitParameter("database.url", "mock://localhost");
tester.start();
try {
// Test successful data retrieval
String request = "GET /api/data?id=1 HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Connection: close\r\n" +
"\r\n";
String response = tester.getResponses(request);
assertTrue(response.contains("HTTP/1.1 200 OK"));
assertTrue(response.contains("{\"id\":\"1\",\"data\":\"Test data 1\"}"));
// Test missing data
request = "GET /api/data?id=999 HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Connection: close\r\n" +
"\r\n";
response = tester.getResponses(request);
assertTrue(response.contains("HTTP/1.1 404"));
// Test missing parameter
request = "GET /api/data HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Connection: close\r\n" +
"\r\n";
response = tester.getResponses(request);
assertTrue(response.contains("HTTP/1.1 400"));
} finally {
tester.stop();
}
}
@Test
public void testDataSaving() throws Exception {
ServletTester tester = new ServletTester("/api");
ServletHolder dataServlet = tester.addServlet(DataServlet.class.getName(), "/data");
dataServlet.setInitParameter("database.url", "mock://localhost");
tester.start();
try {
String requestBody = "data=New test data";
String request = "POST /api/data HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Content-Type: application/x-www-form-urlencoded\r\n" +
"Content-Length: " + requestBody.length() + "\r\n" +
"Connection: close\r\n" +
"\r\n" +
requestBody;
String response = tester.getResponses(request);
assertTrue(response.contains("HTTP/1.1 200 OK"));
assertTrue(response.contains("\"status\":\"saved\""));
} finally {
tester.stop();
}
}
}// Session-aware servlet
public class SessionServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
HttpSession session = req.getSession();
Integer count = (Integer) session.getAttribute("count");
if (count == null) {
count = 0;
}
count++;
session.setAttribute("count", count);
resp.setContentType("text/plain");
resp.getWriter().println("Visit count: " + count);
resp.getWriter().println("Session ID: " + session.getId());
}
}
// Session test
public class SessionTest {
@Test
public void testSessionHandling() throws Exception {
ServletTester tester = new ServletTester();
// Enable sessions in the context
tester.getContext().setSessionHandler(new SessionHandler());
tester.addServlet(SessionServlet.class, "/session");
tester.start();
try {
// First request - should create session
String request1 = "GET /session HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Connection: close\r\n" +
"\r\n";
String response1 = tester.getResponses(request1);
assertTrue(response1.contains("Visit count: 1"));
// Extract session cookie
String sessionCookie = extractSessionCookie(response1);
assertNotNull(sessionCookie);
// Second request with session cookie
String request2 = "GET /session HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Cookie: " + sessionCookie + "\r\n" +
"Connection: close\r\n" +
"\r\n";
String response2 = tester.getResponses(request2);
assertTrue(response2.contains("Visit count: 2"));
} finally {
tester.stop();
}
}
private String extractSessionCookie(String response) {
String[] lines = response.split("\r\n");
for (String line : lines) {
if (line.startsWith("Set-Cookie: JSESSIONID=")) {
return line.substring("Set-Cookie: ".length());
}
}
return null;
}
}import jakarta.servlet.AsyncContext;
import jakarta.servlet.ServletException;
import java.util.concurrent.CompletableFuture;
// Async servlet implementation
public class AsyncServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// Start async processing
AsyncContext asyncContext = req.startAsync();
asyncContext.setTimeout(5000); // 5 second timeout
// Process request asynchronously
CompletableFuture.runAsync(() -> {
try {
// Simulate long-running operation
Thread.sleep(1000);
HttpServletResponse response = (HttpServletResponse) asyncContext.getResponse();
response.setContentType("text/plain");
response.getWriter().println("Async operation completed");
asyncContext.complete();
} catch (Exception e) {
asyncContext.complete();
}
});
}
}
// Async servlet test
public class AsyncServletTest {
@Test
public void testAsyncProcessing() throws Exception {
ServletTester tester = new ServletTester();
// Configure servlet with async support
ServletHolder asyncServlet = tester.addServlet(AsyncServlet.class.getName(), "/async");
asyncServlet.setAsyncSupported(true);
tester.start();
try {
String request = "GET /async HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Connection: close\r\n" +
"\r\n";
// Use timeout for async response
String response = tester.getResponses(request, 10, TimeUnit.SECONDS);
assertTrue(response.contains("HTTP/1.1 200 OK"));
assertTrue(response.contains("Async operation completed"));
} finally {
tester.stop();
}
}
}// Base test class for servlet integration tests
public abstract class ServletIntegrationTestBase {
protected ServletTester tester;
@Before
public void setUp() throws Exception {
tester = createServletTester();
configureServletTester(tester);
tester.start();
}
@After
public void tearDown() throws Exception {
if (tester != null) {
tester.stop();
}
}
protected ServletTester createServletTester() {
return new ServletTester("/test");
}
protected abstract void configureServletTester(ServletTester tester);
// Utility methods for common test operations
protected String sendGetRequest(String path) throws Exception {
return sendRequest("GET", path, null, null);
}
protected String sendPostRequest(String path, String body) throws Exception {
return sendRequest("POST", path, "application/x-www-form-urlencoded", body);
}
protected String sendJsonRequest(String method, String path, String jsonBody) throws Exception {
return sendRequest(method, path, "application/json", jsonBody);
}
protected String sendRequest(String method, String path, String contentType, String body)
throws Exception {
StringBuilder request = new StringBuilder();
request.append(method).append(" ").append(tester.getContextPath()).append(path)
.append(" HTTP/1.1\r\n");
request.append("Host: localhost\r\n");
if (contentType != null) {
request.append("Content-Type: ").append(contentType).append("\r\n");
}
if (body != null) {
request.append("Content-Length: ").append(body.length()).append("\r\n");
}
request.append("Connection: close\r\n");
request.append("\r\n");
if (body != null) {
request.append(body);
}
return tester.getResponses(request.toString());
}
// Response parsing utilities
protected int getResponseStatus(String response) {
String statusLine = response.split("\r\n")[0];
String[] parts = statusLine.split(" ");
return Integer.parseInt(parts[1]);
}
protected String getResponseBody(String response) {
int bodyStart = response.indexOf("\r\n\r\n");
if (bodyStart == -1) return "";
return response.substring(bodyStart + 4);
}
protected String getResponseHeader(String response, String headerName) {
String[] lines = response.split("\r\n");
for (String line : lines) {
if (line.toLowerCase().startsWith(headerName.toLowerCase() + ":")) {
return line.substring(headerName.length() + 1).trim();
}
}
return null;
}
}
// Example usage of integration test framework
public class ApiServletIntegrationTest extends ServletIntegrationTestBase {
@Override
protected void configureServletTester(ServletTester tester) {
// Add authentication filter
tester.addFilter(AuthenticationFilter.class, "/api/*",
EnumSet.of(DispatcherType.REQUEST));
// Add API servlets
ServletHolder userServlet = tester.addServlet(UserServlet.class.getName(), "/api/users/*");
userServlet.setInitParameter("database.url", "mock://test");
ServletHolder dataServlet = tester.addServlet(DataServlet.class.getName(), "/api/data/*");
dataServlet.setInitParameter("cache.enabled", "false");
}
@Test
public void testUserApiEndpoints() throws Exception {
// Test user creation
String createRequest = "{\"name\":\"John Doe\",\"email\":\"john@example.com\"}";
String response = sendJsonRequest("POST", "/api/users", createRequest);
assertEquals(201, getResponseStatus(response));
assertTrue(getResponseBody(response).contains("\"id\":"));
// Test user retrieval
response = sendGetRequest("/api/users/1");
assertEquals(200, getResponseStatus(response));
assertTrue(getResponseBody(response).contains("John Doe"));
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-eclipse-jetty--jetty-servlet