0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Java100行弱で10分以内に読める!Tomcat無しでサーブレットを実行する最小コンテナを自作してみた第2回web.xml導入

Last updated at Posted at 2025-08-26

前回まで

Javaによる簡易HTTPサーバの作り方
Java100行弱で10分以内に読める!Tomcat無しでサーブレットを実行する最小コンテナを自作してみた

今回の実装

web.xmlからパスとサーブレットのクラス名を取得するようにした。
一気にコンテナっぽくなった

MiniContainer.js
        //...略...
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.parse(new File("web.xml"));

        NodeList list = doc.getElementsByTagName("servlet");
        for (int i = 0; i < list.getLength(); i++) {
            Element el = (Element) list.item(i);
            String path = el.getAttribute("path");
            String clazz = el.getAttribute("class");
            addServletFromClass(path, clazz);
        }
        //...略...
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
    <servlet path="/hello" class="HelloServlet"/>
    <servlet path="/bye" class="ByeServlet"/>
</web-app>

動作

見た目は前回までと特に変わらない。
image.png

image.png

コンテナの実装

MiniContainer.js
import java.io.*;
import java.net.*;
import java.util.*;
import java.lang.reflect.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Element;

public class MiniContainer {
    private static final Map<String, MyServlet> routes = new HashMap<>();

    public static void main(String[] args) throws Exception {
    
        // ユーザが作ったServletクラスをロードして登録
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.parse(new File("web.xml"));

        NodeList list = doc.getElementsByTagName("servlet");
        for (int i = 0; i < list.getLength(); i++) {
            Element el = (Element) list.item(i);
            String path = el.getAttribute("path");
            String clazz = el.getAttribute("class");
            addServletFromClass(path, clazz);
        }
        //web.xml導入前
        //addServletFromClass("/hello", "HelloServlet");
        //addServletFromClass("/bye", "ByeServlet");

        ServerSocket server = new ServerSocket(8081);
        System.out.println("Running on http://localhost:8081/");

        while (true) {
            Socket client = server.accept();
            handleClient(client);
        }
    }

    // クラス名からユーザのサーブレットをロードして登録
    private static void addServletFromClass(String path, String className) throws Exception {
        Class<?> clazz = Class.forName(className); // クラスをロード
        MyServlet servlet = (MyServlet) clazz.getDeclaredConstructor().newInstance();
        routes.put(path, servlet);
    }

    private static void handleClient(Socket client) throws Exception {
        InputStream in = client.getInputStream();
        OutputStream out = client.getOutputStream();

        // ブラウザから送られてくるHTTPリクエスト1行目を読む
        char[] buf = new char[1024];
        int idx = 0;
        int c;
        while ((c = in.read()) != -1) {
            if (c == '\r') continue; // HTTPではCRLFなのでCRは無視
            if (c == '\n') break; // LFで1行終わり
    	    buf[idx++] = (char)c;
        }
        String requestLine = new String(buf, 0, idx);
        System.out.println("Request: " + requestLine);

        // 例 "GET /hello HTTP/1.1" → ["GET", "/hello", "HTTP/1.1"]
        // "/hello"だけ取り出す
        String path = "/";
        if (!requestLine.isEmpty()) {
            path = requestLine.split(" ")[1];
        }

        MyServlet servlet = routes.get(path);
        if (servlet != null) {
            servlet.service(in, out); // ユーザが作ったサーブレットを呼び出す
        } else {
            String body = "<h1>404 Not Found</h1>";
            String headers = "HTTP/1.1 404 Not Found\r\n" +
                             "Content-Length: " + body.getBytes("UTF-8").length + "\r\n" +
                             "Content-Type: text/html; charset=UTF-8\r\n\r\n";
            out.write(headers.getBytes("UTF-8"));
            out.write(body.getBytes("UTF-8"));
        }

        out.flush();
        client.close();
    }
}
MyServlet.java
import java.io.*;

public interface MyServlet {
    void service(InputStream in, OutputStream out) throws Exception;
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
    <servlet path="/hello" class="HelloServlet"/>
    <servlet path="/bye" class="ByeServlet"/>
</web-app>

サーブレット

HelloServlet.java
import java.io.*;

public class HelloServlet implements MyServlet {
    @Override
    public void service(InputStream in, OutputStream out) throws Exception {
        String body = "<html><body><h1>Hello from HelloServlet!</h1></body></html>";
        String headers = "HTTP/1.1 200 OK\r\n" +
                         "Content-Length: " + body.getBytes("UTF-8").length + "\r\n" +
                         "Content-Type: text/html; charset=UTF-8\r\n\r\n";
        out.write(headers.getBytes("UTF-8"));
        out.write(body.getBytes("UTF-8"));
    }
}
ByeServlet.java
import java.io.*;

public class ByeServlet implements MyServlet {
    @Override
    public void service(InputStream in, OutputStream out) throws Exception {
        String body = "<html><body><h1>Goodbye!</h1></body></html>";
        String headers = "HTTP/1.1 200 OK\r\n" +
                         "Content-Length: " + body.getBytes("UTF-8").length + "\r\n" +
                         "Content-Type: text/html; charset=UTF-8\r\n\r\n";
        out.write(headers.getBytes("UTF-8"));
        out.write(body.getBytes("UTF-8"));
    }
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?