MiniCat

Basic Introduction

We will hand-write a Mini version of Tomcat. What we implement is a server software that can receive HTTP requests from browser clients, process them through MiniCat, and return the processing results to the browser client.

  • Provide service, accept requests (Socket communication)
  • Request information encapsulated as Request object (Response object)
  • Client requests resources, resources divided into static resources (HTML) and dynamic resources (Servlet)
  • Return resources to client browser

Tomcat’s Core Functions

Tomcat is a Servlet container, core responsibilities include:

  • Receive HTTP requests from browser
  • Dispatch requests to corresponding Servlet
  • Manage Servlet lifecycle
  • Return response to browser

Minimum Function Modules

To implement the most basic functions, MiniCat needs at least the following modules:

Socket Server Listener

  • Use ServerSocket to receive browser requests
  • Multi-threaded processing of concurrent requests

Request Parser

  • Parse HTTP request (request line, request headers, request body)
  • Build MiniRequest object, encapsulate request information

Response Builder

  • Build HTTP response
  • Include response status, response headers, response body

Servlet Interface and Management

  • Custom MiniServlet interface (simulating javax.servlet.Servlet)
  • Load and initialize Servlet
  • Call service() method to process requests

URL Mapping Dispatcher

  • Maintain mapping between URL and Servlet
  • Match corresponding Servlet instance based on request path

WebApp Configuration Loader

  • Read web.xml (simplified) or custom configuration file
  • Dynamically load classes (using Class.forName)

Prepare Dependencies

<dependencies>
    <dependency>
        <groupId>dom4j</groupId>
        <artifactId>dom4j</artifactId>
        <version>1.6.1</version>
    </dependency>
    <dependency>
        <groupId>jaxen</groupId>
        <artifactId>jaxen</artifactId>
        <version>1.1.6</version>
    </dependency>
</dependencies>

Bootstrap Code

public class Bootstrap {
    private int port = 8080;

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public static void main(String[] args) {
        Bootstrap bootstrap = new Bootstrap();
        try {
            bootstrap.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void start() throws Exception {
        loadServlet();

        int corePoolSize = 10;
        int maximumPoolSize = 20;
        long keepAliveTime = 100L;
        TimeUnit unit = TimeUnit.SECONDS;

        BlockingQueue<Runnable> deque = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                deque,
                threadFactory,
                handler
        );

        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("ServerSocket: " + serverSocket);
        while (true) {
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket, servletMap);
            threadPoolExecutor.execute(requestProcessor);
        }
    }

    private Map<String, HttpServlet> servletMap = new HashMap<>();

    private void loadServlet() {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();
            List<Element> selectNodes = rootElement.selectNodes("//servlet");
            for (int i = 0; i < selectNodes.size(); i ++) {
                Element element = selectNodes.get(i);

                Element servletElement = (Element) element.selectSingleNode("servlet-name");
                String servletName = servletElement.getStringValue();

                Element servletClassElement = (Element) element.selectSingleNode("servlet-class");
                String servletClass = servletClassElement.getStringValue();

                Element servletMapping = (Element) rootElement
                        .selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
                servletMap.put(urlPattern, (HttpServlet) Class.forName(servletClass).newInstance());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Code Analysis

Main Entry

Create Bootstrap instance, call start() to start server, catch exceptions and output.

Startup Logic

Call loadServlet() method, load custom Servlet configuration (parse mapping from web.xml).

Create Thread Pool

  • Core pool size: 10, maximum pool size: 20
  • Keep-alive time for idle threads: 100 seconds
  • Task queue: 50
  • Rejection policy: Default throw exception (AbortPolicy)

Create ServerSocket and Listen for Requests

ServerSocket serverSocket = new ServerSocket(port);
System.out.println("ServerSocket: " + serverSocket);

Main Loop

  • accept() blocks until browser request arrives
  • Create new RequestProcessor instance to process request
  • Execute asynchronously using thread pool

Load Configuration

Read web.xml file from classpath, parse XML document using SAXReader.


Temporary Summary

Bootstrap, as MiniCat’s core startup class, completes:

  • Load Servlet classes configured in configuration file web.xml
  • Initialize Servlet and establish URL mapping
  • Start Socket service listening for HTTP requests
  • Dispatch a thread for each request to execute
  • Thread actually uses RequestProcessor for business processing