1. Webアプリケーションの標準ディレクトリ構成
Tomcatをはじめとするサーブレットコンテナで動作させるJava Webアプリケーションは、Servlet/Jakarta EE仕様で定められた階層構造に従って配置する必要があります。デプロイ先のwebappsフォルダ配下にプロジェクト名(例:inventory_system)のディレクトリを作成し、以下の構成を厳密に守って構築します。
- WEB-INF/:ディレクトリ名は大文字で固定。この配下に配置されたファイルはクライアントから直接アクセスできず、セキュリティ的に保護されます。
- WEB-INF/classes/:Javaソースコードをコンパイルして生成された
.classファイル(バイトコード)を格納する領域。 - WEB-INF/lib/:サードパーティ製ライブラリやJDBCドライバなどの
.jarファイルを配置するディレクトリ。依存関係が必要な場合にのみ作成します。 - WEB-INF/web.xml:デプロイメント記述子。URLパターンと対応するサーブレットクラスの紐付けを定義する必須ファイルです。
静的リソース(HTML、CSS、JavaScript、画像など)は、WEB-INFディレクトリの外側に配置することでブラウザから直接参照可能になります。
2. デプロイメント記述子(web.xml)の設定
web.xmlはJakarta EE 9以降の仕様に準拠した名前空間を使用します。リクエストパスとサーブレット実装クラスを関連付けるには、<servlet>でクラス名を登録し、<servlet-mapping>でエンドポイントを設定します。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0"
metadata-complete="true">
<servlet>
<servlet-name>system-init</servlet-name>
<servlet-class>InventoryProcessor</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>system-init</servlet-name>
<url-pattern>/execute</url-pattern>
</servlet-mapping>
</web-app>
3. サーブレットインタフェースの実装とコンパイル
サーブレットはjakarta.servlet.Servletインタフェースを実装したPOJOクラスです。Jakarta EE 9よりパッケージ名がjavax.*からjakarta.*へ移行した点に注意が必要です。開発環境でコンパイルを通すには、Tomcat提供のservlet-api.jarをクラスパスに追加する必要があります。
以下の実装例では、インタフェースのメソッドシグネチャを維持しつつ、状態管理とレスポンス生成のロジックを簡潔に再構成しています。
import jakarta.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;
public class InventoryProcessor implements Servlet {
private ServletConfig runtimeConfig;
@Override
public void init(ServletConfig config) throws ServletException {
this.runtimeConfig = config;
}
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
System.out.println("リクエスト受領: サーブレット処理を開始");
// レスポンスヘッダーの設定
res.setContentType("text/html; charset=UTF-8");
// 出力ストリームの取得とデータ送信
PrintWriter outputStream = res.getWriter();
outputStream.println("<!DOCTYPE html><html lang='ja'>");
outputStream.println("<body>");
outputStream.println("<h2>在庫処理モジュール実行完了</h2>");
outputStream.println("<p>正常にリクエストが処理されました。</p>");
outputStream.println("</body></html>");
outputStream.flush();
}
@Override
public void destroy() {
this.runtimeConfig = null;
}
@Override
public String getServletInfo() {
return "Inventory Management Servlet";
}
@Override
public ServletConfig getServletConfig() {
return this.runtimeConfig;
}
}
コンパイルが完了したら、生成されたInventoryProcessor.classをWEB-INF/classesディレクトリに配置します。
4. リクエスト処理の内部フロー
ブラウザからhttp://localhost:8080/inventory_system/executeへアクセスした場合、コンテナ内部では以下の順序で処理が進行します。
- Tomcatが着信リクエストを受信し、パスからコンテキストルート(
/inventory_system)を特定。 - 対象アプリケーションの
WEB-INF/web.xmlを解析し、/executeに紐付くサーブレット名(system-init)を検索。 - 対応するクラス名(
InventoryProcessor)を特定し、Javaリフレクションを用いてインスタンスを生成(初回リクエスト時)。 - インスタンスの
init()メソッドを実行した後、service()メソッドへリクエストとレスポンスオブジェクトを渡してビジネスロジックを起動。 - 処理終了後、生成されたHTMLデータがクライアントへ返送される。
この仕組みにより、開発者はmainメソッドやソケット通信を明示的に記述せず、リクエスト/レスポンスのハンドリングロジックのみを実装すれば動作します。
5. サーバーログの文字化け対策と応答制御
Windows環境においてTomcatの標準出力が文字化けする場合、conf/logging.properties内のコンソールハンドラー設定を修正します。システムロケールに合わせてエンコーディングを指定することでログの可読性が向上します。
java.util.logging.ConsoleHandler.encoding = UTF-8
クライアント側への出力では、ServletResponse.setContentType()でMIMEタイプと文字コードを事前に設定することが必須です。これを省略すると、ブラウザがHTMLソースをそのままテキストとして表示したり、文字コード推測に失敗したりする原因となります。PrintWriterまたはServletOutputStreamを通じてストリーム書き込みを行うことで、動的なコンテンツ生成が可能になります。