Jakarta EE 10 - Jakarta Servlet 6.0 変更内容まとめ

はじめに

Jakarta EE 10 で Jakarta Servlet は 5.0 から 6.0 へバージョンアップします。

アプリケーション開発者から見ると、ServletConnection インターフェース追加と、Cookie への属性追加が主な変更点になるでしょう。

以下に見ていきます。


リクエスト/接続の一意な識別子が取得可能となった

非同期処理や接続を共有したリクエスト(HTTP keep-alive, HTTP upgrade)などにおいて、リクエストや接続を特定する識別子が取得できるようになりました。

以下の ServletConnection インターフェースが追加されました。

public interface ServletConnection {

    String getConnectionId();

    String getRequestId();

    String getProtocol();

    String getProtocolConnectionId();

    String getProtocolRequestId();

    boolean getTLS();
}

これにより、非同期処理、接続を共有したリクエスト(HTTP keep-alive, HTTP upgrade)、HTTP/2接続とストリーム、WebSocketセッションID などを特定できるようになり、デバッグ作業の助けになります(用途としてはあくまでデバッグ用途ということです)。

ServletConnection は、ServletRequest#getServletConnection() から取得できます。

public interface ServletRequest {
  ServletConnection getServletConnection();
}


セッションクッキーへの汎用的な属性サポート追加

Cookie に以下のメソッドが追加されました。

public class Cookie implements Cloneable, Serializable {

  public void setAttribute(String name, String value) {
  }

  public String getAttribute(String name) {
  }

  public Map<String, String> getAttributes() {
  }

}

SameSite などの属性を設定することができるようになりました。

Faces 4.0 側でも ExternalContext#addResponseCookie() にて、この属性対応が入っています。


HttpServlet.doHead() のデフォルト実装のレスポンス変更

HttpServletdoHead メソッドは、ボディ無し、で ContentLength を設定したレスポンスを返していました。

protected void doHead(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {
  NoBodyResponse response = new NoBodyResponse(resp);
  doGet(req, response);
  response.setContentLength();
}

以下のようなデフォルト実装に変更されました。

仕様上はコンテンツヘッダを省略できることになっているため、デフォルトの実装をそれに合わせた形になります。

protected void doHead(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {
  if (legacyHeadHandling) {
    NoBodyResponse response = new NoBodyResponse(resp);
    doGet(req, response);
    response.setContentLength();
  } else {
    doGet(req, resp);
  }
}

旧来の動作を望む場合は jakarta.servlet.http.legacyDoHead 初期化パラメータにより設定できます。 ただし、NoBodyResponse は合わせて非推奨化されています。


その他の変更内容

  • URI パスのデーコード、正規化仕様の明確化

    • HttpServletRequest#getPathInfo() getContextPath() getServletPath() の URIパスのURLデコードや正規化などに関する仕様が明確化された
  • Cookie 仕様 RFC 2109 から RFC 6265 への変更に伴う Cookie クラスの変更

    • クッキー名の制限「非NULL、非空白でなければならない」に緩和
    • setVersion() はNO-OP, getVersion() は固定で 0 を返す
  • ServletContext.getRealPath() の挙動の明確化

    • パスは / で始まり、現在のコンテキストルートからの相対パスとして解釈される
    • パスが / で始らない場合、コンテナはパスの先頭に / があるものとして動作する
  • X-Powered-By ヘッダの非推奨化

    • X-Powered-By ヘッダを付与することが推奨されていたが、セキュリティ観点でこれが非推奨となった
  • ServletRequest.getRemoteAddr()、他のメソッドの JavaDoc が、経路にリバースプロキシがある場合などの動作について補足された

  • ServletResponse.setCharacterEncoding()null 設定した場合の挙動が明確化された(JavaDoc への加筆)

  • ServletContext の各種 getter 呼び出しの制限緩和

    • 動的に追加されたレシーバーに対しての制限のあるメソッド(ServletContext#getEffectiveMajorVersion() など)
    • JavaDoc にある以下の @throws の記載が削除された

      UnsupportedOperationException - この ServletContext が、web.xml または web-fragment.xml で宣言されておらず、WebListener でアノテーションも付けられていない ServletContextListener の ServletContextListener.contextInitialized(javax.servlet.ServletContextEvent) メソッドに渡された場合

  • Servlet 5.0 以前で非推奨とされた API クラスおよびメソッドの削除

    • SingleThreadModel および HttpSessionContext インターフェイス、HttpUtils クラス、およびさまざまな非推奨のメソッド削除を含む
  • Jakarta Pages 3.1 仕様の変更に合わせて、JspPropertyGroupDescriptor#getErrorOnELNotFound() メソッドが追加

  • ServletInputStream.isReady() ServletOutputStream.isReady() が false を返したときのスケジューリングの影響が Javadoc に明記された

  • 仕様書 6.2.2, “Wrapping Requests and Responses” の要件が緩和された

    • 旧来の仕様

      サーブレットやフィルタから RequestDispatcher.forward や RequestDispatcher.include を呼び出す際、呼び出し元がリクエストやレスポンスオブジェクトを ラップしている場合、ラッパーオブジェクトの同一性の要件も同様に適用されます。 この場合、呼び出されたサーブレットが見るリクエストとレスポンスオブジェクトは、 呼び出したサーブレットやフィルターが渡したのと同じラッパーオブジェクトでなければなりません。

    • 変更後の仕様

      フィルタやサーブレットが RequestDispatcher.forward や RequestDispatcher.include を呼び出すとき、呼び出されたフィルタやサーブレットが見るリクエストオブジェクトやレスポンスオブジェクトは、渡されたオブジェクトと同じラッパーであるか、渡されたオブジェクトのラッパーのどちらかでなければなりません。 startAsync(ServletRequest, ServletResponse) が非同期サイクルの開始に使われた場合、 AsyncContext.dispatch() (またはオーバーロードされた variant) に続くフィルターやサーブレットによって見られるリクエストとレスポンスオブジェクトは、渡されたオブジェクトと同じラッパーか、渡されたオブジェクトのラッパーでなければなりません。

  • module-info.java の追加

module jakarta.servlet {
    exports jakarta.servlet;
    exports jakarta.servlet.annotation;
    exports jakarta.servlet.descriptor;
    exports jakarta.servlet.http;

    opens jakarta.servlet.resources;
}