前回まで
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>
動作
コンテナの実装
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"));
}
}