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