Skip to main contentLogo

Command Palette

Search for a command to run...

Servlet and Embedded Jetty — What Happens Behind the Scenes

Published on
Apr 8, 2025
Servlet and Embedded Jetty — What Happens Behind the Scenes

Introduction

After strengthening your Java SE knowledge, stepping into the backend world with Servlets and then Spring/Spring Boot is a very sensible path.

Understanding the workings of the Servlet API and servlet containers like Jetty will help you grasp the foundations behind many behaviors of Spring Boot that sometimes seem “magical”.

In this article, as a backend developer, we will focus on the behind-the-scenes processes.


Key Concepts: Servlet and Servlet Container

Servlet

  • A Java class designed to respond to requests over the HTTP protocol.
  • Used to generate dynamic web content.
  • Implements the javax.servlet.Servlet interface or extends the javax.servlet.http.HttpServlet abstract class.

Servlet Container (Web Container/Web Server)

  • Software that manages the lifecycle of servlets.
  • Receives HTTP requests and routes them to the appropriate servlet.
  • Handles low-level tasks like network communication, thread management, and more.
  • Examples include Jetty, Tomcat, Undertow. Spring Boot comes with Tomcat by default, but Jetty and Undertow can also be configured.

Jetty

Jetty is an open-source web server and servlet container developed by the Eclipse Foundation.

It is especially popular in embedded systems and microservice architectures:

  • Lightweight and fast: Low memory usage and fast start-up.
  • Embeddable: Can be included in a Java application (can be started with the main() method).
  • Flexible and extensible: Add/remove modules as needed.
  • Asynchronous support: Supports Servlet 3.0+, WebSocket, NIO.

Request/Response Flow

Listening

  • When Jetty starts, it begins listening on the configured port (e.g., 8080).
  • The ServerConnector component opens a TCP socket.

Connection Acceptance

  • The client requests a connection → ServerConnector accepts the connection.

Thread Allocation

  • Jetty uses a Thread Pool (e.g., QueuedThreadPool).
  • An available thread is assigned to the request and returned to the pool when the work is done.

Data Reading & HTTP Parsing

  • Raw HTTP data is read from the TCP socket.
  • Jetty’s HTTP Parser splits this data into parts like method, URL, headers, and body.

Request and Response Objects

  • Jetty creates HttpServletRequest and HttpServletResponse objects.
  • request → contains all information about the request
  • response → used to construct the reply

Handler Chain & Routing

Incoming requests in Jetty pass through a Handler chain:

  • ServletContextHandler or WebAppContextmaps the URL to the servlet.

Mapping can be defined in the following ways:

  • web.xml – classic configuration with an XML file
  • Annotations@WebServlet("/path") annotations

Servlet Lifecycle – init()

  • When a servlet is called for the first time:
    • It is loaded and the object is created.
    • The init(ServletConfig) method is called only once.
    • Configuration and ServletContext are passed to the servlet.

Processing the Request – service()

  • Jetty calls the service(request, response) method.

If the HttpServlet class is used

  • service() → invokes doGet(), doPost(), doPut(), etc., according to the HTTP method.

Typical doGet() or doPost() logic

  • You read data using request.getParameter(), getHeader(), getInputStream().
  • Business logic is executed (DB query, API call, etc.).
  • On the response object:
    • setStatus()
    • setContentType()
    • setHeader()
    • Write the response with getWriter().println().

Response Commit & Sending

  • When the servlet finishes the service() method:
    • Jetty sends the data in the response to the client in HTTP format.
    • Once commit happens, headers are sent and can no longer be modified.

Thread Release

  • Returned to the thread pool and becomes ready for the next request.

Connection Close / Keep-Alive

  • If Connection: keep-alive is present, the connection is kept alive.
  • Otherwise, the TCP connection is closed.

Jetty’s Thread Model: Parallel Processing

Thread Pool

  • Jetty uses QueuedThreadPool for performance.

Purpose

  • Instead of creating a new thread for each request, existing threads are reused.

How it Works

  • When a request arrives, a thread is taken from the pool.
  • After the work is done, it is returned.
  • If all threads are busy, requests go into a queue.

Potential Problem: Pool Exhaustion

  • During many long-running requests:
    • Threads become full
    • The queue grows
    • Latency increases

➡️ Solution: The pool size can be configured.


Real-case Code Example

servlet/SalamServlet.java
// @WebServlet("/salam") // <<<=== ANNOTATION is the modern approach but we did not use it
public class SalamServlet extends HttpServlet { 

    // Called only once when the servlet is first loaded.
    @Override
    public void init() throws ServletException {
        System.out.println("SalamServlet is starting (programmatic registration)...");
        super.init();
    }

    // Processes incoming HTTP GET requests.
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {

        // 1. Read data from the request (e.g., query parameter)
        String ad = request.getParameter("name");
        if (ad == null || ad.trim().isEmpty()) {
            ad = "Dünya"; // Default value
        }

        // 2. Prepare the response
        response.setContentType("text/html; charset=UTF-8"); 
        response.setCharacterEncoding("UTF-8");
        PrintWriter out = response.getWriter();

        // 3. Create the response body (HTML)
        out.println("<!DOCTYPE html>");
        out.println("<html>");
        out.println("<head>");
        out.println("<title>Salam Embedded Jetty</title>");
        out.println("<style>body { font-family: sans-serif; }</style>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h1>Salam, " + ad + "! (Embedded Jetty)</h1>");
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss");
        out.println("<p>Server Time: " + LocalDateTime.now().format(formatter) + "</p>");
        out.println("<p>Servlet Path: " + request.getServletPath() + "</p>");
        // Show the full request URL
        out.println("<p>Request URI: " + request.getRequestURI() + "</p>");
        out.println("<p>Your Browser (User-Agent): " + request.getHeader("User-Agent") + "</p>");
        out.println("</body>");
        out.println("</html>");

        System.out.println("GET request processed for '" + ad + "' (Embedded).");
    }

    // Processes incoming HTTP POST requests
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
         System.out.println("POST request received (Embedded), redirecting to doGet...");
         doGet(request, response);
    }

    // Called when the servlet container is stopped or the application is shut down.
    @Override
    public void destroy() {
        System.out.println("SalamServlet is stopping (programmatic registration)...");
        super.destroy();
    }
}
EmbeddedJettyMain.java
public class EmbeddedJettyMain {

    public static void main(String[] args) throws Exception {
        // 1. Create a Jetty Server object (will listen on port 8080)
        Server server = new Server(8080); 
        System.out.println("Jetty server is starting on port 8080...");

        // 2. Create a Servlet Context Handler (with SESSIONS support)
        // This is the main container of our web application
        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
        // Set the context path ("/" denotes the root path)
        context.setContextPath("/");
        // Add the created context handler to the server
        server.setHandler(context);

        // 3. Add servlets to the Context Handler
        //    - Create SalamServlet and "package" it with ServletHolder
        //    - Map it to the "/salam/*" URL pattern (captures /salam or /salam/abc etc.)
        context.addServlet(new ServletHolder(new SalamServlet()), "/salam/*"); 
        System.out.println("SalamServlet registered for path '/salam/*'.");

        // Additional servlets can be added similarly:
        // context.addServlet(new ServletHolder(new AnotherServlet()), "/another/*");
        // context.addServlet(new ServletHolder(new AdminServlet()), "/admin/*");

        // 4. Start the server
        server.start(); 
        System.out.println("Jetty server started successfully!");
        System.out.println("Check http://localhost:8080/salam or http://localhost:8080/salam?ad=YourName in your browser.");

        // 5. Wait for the server to finish (prevents the main thread from exiting immediately)
        server.join();
        System.out.println("Jetty server stopped.");
    }
}
Thanks for reading.