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無しでサーブレットを実行する最小コンテナを自作してみた

Last updated at Posted at 2025-08-24

初めに

前回JavaでHTTPサーバを実装しました。
https://qiita.com/earthen94/items/4d4906fbb1b1f93eedc9

今回はこれを拡張し、簡単なJAVAサーブレットを動かせるようにしました。
見通しを良くする為にかなり短くしています。
POSTやパラメータ取得は次回に譲ります。

実行

javac MiniContainer.java MyServlet.java HelloServlet.java ByeServlet.java
java MiniContainer

ブラウザ上で以下を入力

http://localhost:8081/hello
http://localhost:8081/bye

image.png
image.png
image.png

実装部分

サーブレットのインターフェース

どんなサーブレットでもこのメソッドを実装するとコンテナ側から型を気にしなくてよくなる。

MyServlet.java
import java.io.*;

public interface MyServlet {
    void service(InputStream in, OutputStream out) throws Exception;
}

コンテナ

MiniContainer.java
import java.io.*;
import java.net.*;
import java.util.*;
import java.lang.reflect.*;

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

    public static void main(String[] args) throws Exception {
        // ユーザが作ったServletクラスをロードして登録
        // Tomcatであればここはweb.xmlに登録する
        addServletFromClass("/hello", "HelloServlet");
        addServletFromClass("/bye", "ByeServlet");

        // ポート8081で待ち受ける。IPアドレスは内部的にOSから取得するので不要
        ServerSocket server = new ServerSocket(8081);
        System.out.println("Running on http://localhost:8081/");

        // ここを無限ループにしないと1リクエストしか受け付けられない
        // (一回ブラウザから開いたらサーバーが閉じられてしまう)
        while (true) {
            Socket client = server.accept();//ポート8081に来るTCP接続を待つ
            handleClient(client);
        }
    }

    // クラス名からユーザのサーブレットをロードして登録
    private static void addServletFromClass(String path, String className) throws Exception {
        Class<?> clazz = Class.forName(className); // クラスをロード
        // サーブレットのインスタンスを作成
        // clazz.getDeclaredConstructor()は引数無しコンストラクタ
        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(); // サーブレットの中でoutの中に詰めたHTTPヘッダ及びHTMLを送信
        client.close();
    }
}

サーブレット

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"));
    }
}

手稿

整理する為に書いた。せっかくなので貼る。
f6f5f38f91ded.jpg

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?