Servlet 6.0(Jakarta EE 10)の変更点まとめ

Jakarta EE 10でServletのバージョンが6.0になりました。 Java EE 8ではServletのバージョンは4.0で、そのあとの5.0はパッケージ名の変換のみだったので、 久しぶりの意味のある仕様変更になります。

ということでまとめ。 ソースを読んで差分をざっくり眺めてるだけなので、間違い、抜け漏れは当然あります。あれ?と思った場所があったら教えてください。

github.com

全般

全般的にHTTP1.1準拠のRFCRFC 2616からRFC 7231に変更されました。

RFC 2616 - Hypertext Transfer Protocol -- HTTP/1.1

RFC 7231 - Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content

それ以外も枝葉のRFCが全般的に変更されています。

また、過去のServlet APIで非推奨とされていたメソッド類、クラス類が削除されています。 以下のクラス類は削除されました。

  • HttpSessionContext
  • HttpUtils
  • SingleThreadModel

非推奨とされていたメソッドは、例えば以下のようなものです。

  • HttpSession#setValue()
  • HttpSession#getValue()

これらは属性のスコープの概念が取り入れられたときにattributeという名前でメソッドが置き換えられていて誰も使ってなかったはずなので、特に問題はないと思われます。 J2EE 1.3以前から生き残ってるシステムは知らね。

消されたであろうメソッドの一覧は以下に。

非推奨のリスト (Java Servlet 4.0)

いくつかのメソッドで例外的な操作が明文化されています。

例えば、nullが渡された時の挙動が明文化されているものがあります。一度値を渡してしまった後に元に戻すためにはどうすればいいの?みたいなのに答えてるのかもしれません。

APサーバーによっては挙動が変更されている(例外が発生する、何も起こらない等)かもしれないので、実際に使うときは必ずjavadocを確認するようにしてください。

HttpServletRequest

getRequestId()メソッドが追加されました。サーブレットコンテナ内で実装依存の一意の識別子を返します。マイクロサービスでリクエストの遷移を追いたいときに使えるのかもしれません。

getProtocolRequestId()が追加されました。HTTP1.X/2/3 OR AJPその他を識別するのに使用できます。

ServletConnection getServletConnection()メソッドが追加されました。ServletConnectionについては後述。(とはいっても有用なものではない)

Cookie

クッキー準拠のRFCRFC 2109からRFC 6265に変更されました。

クッキーにコメント、バージョンが入れられなくなったり、新しいRFCで追加された属性のアクセサーが生えたりしました。

また、自由な属性名でクッキーに値を入れられるようにsetAttribute(String name, String value)メソッドが追加されました。*1

セキュリティの都合等でクッキーには属性が追加されることが多々あり、 新しい属性を使わないといけない場合はCookieクラスを使えずに直接HttpServletResponse#setHeader(String name, String value)を呼び出さなければいけなかったことがこれで解消されそうです。

HttpServlet

doHeadメソッドの挙動が変更され、デフォルトでdoGetメソッドと同じ動作をするようになりました。

これが問題である場合はServletConfigの初期化パラメーターを操作して、jakarta.servlet.http.legacyDoHeadをtrueにする必要があります。

なお、今まではdoGetを実行しながらレスポンスボディを返さないという挙動をしていました。

jsp-property-groupにErrorOnELNotFound属性が追加。

EL式の挙動を変更します。詳しくは以下。

https://github.com/eclipse-ee4j/jsp-api/issues/40

ServletConnection

今回増えた唯一のクラスです。

javadocによると、デバッグ用途で使われることを想定しており、サーブレットコンテナから見た生の接続情報を提供してくれるそうです。

しかしながら現時点で取得できるのはあまり多くはないため、今後増えていくのかもしれません。

まとめ

ということで、駆け足ながら差分をざっとまとめてみました。 他にこういう差分があるよというのを見つけたら教えてください。

*1:たぶん今回の変更の中で一番うれしい

Jakarta EEって現状どうなってるんだっけ?と思ったら次のリリースがすぐだったので各社のJakarta EE仕様準拠状況をまとめた。

Jakarta EE 10が2022年第一四半期にリリースされます。つまりもうすぐです。 そういえば、Java EEってどうなったんだっけ?とか、Jakarta EEになってからどうなってるの?という人向けのまとめ記事です。

で、Jakarta EEって現状どうなってるんだっけ?ってまとめようとしたら、以下のページがいい感じにまとまっていたので、こちらを参照するとよい感じです。 https://openliberty.io/docs/22.0.0.2/jakarta-ee.html

重要なところだけ抜粋すると、

Jakarta EEプラットフォームの継続的な開発は、JESP(Jakarta EE Specification Process)を通じてJakarta開発者コミュニティによって進められ、プラットフォーム向けの新しい仕様プロジェクトを導入するための青写真が提供されています。Open LibertyなどのJakarta EE互換ランタイムは、仕様プロジェクトで記述された機能を実装しています。各実装はTechnology Compatibility Kit(TCK)を通じてコミュニティによって検証されます。TCKは、リファレンス実装の準拠性を確認するためのテスト群です。ある仕様プロジェクトが正式な仕様となるには、TCKのテストを満たすオープンソースのリファレンス実装を少なくとも1つ開発するプロセスを経て、承認される必要があります。

Open Libertyは、Jakarta EE 9.1のリファレンス実装であり、Jakarta EE Platform Specificationで定義されたJakarta EEの必須仕様を全て実装していることにあんります。Open Liberty上で動作するアプリケーションは、サーバー構成で対応するOpen Libertyの機能を有効にすることで、Jakarta EE APIを利用することができる。次の表は、最もよく使われる Jakarta EE 仕様をいくつか説明し、それに対応する Open Liberty 機能をリストアップしたものです。Try it outの欄には、Open Libertyがその仕様をどのように実装しているかを示すOpen Libertyガイドへのリンクがあります。

ということで、RIがGlassFishじゃなくて、Open Liberty(IBMのWebSphereのコミュニティ版)になってました。まじかー。

ちなみに、Jakarta EEの直近のリリースとしては以下のような感じになります。カッコ内はリリース日です。

  • Java EE 8(2017/7) Oracle がリリースした最後のJava EE
  • Jakarta EE 8(2019/9) ブランド名の変更のみ。Java EE 8と変更なし。
  • Jakarta EE 9(2020/11) パッケージ名の変更*1 およびにいくつかの機能の追加及びに削除。Jakarta EE 8以前との互換性が問題になる。※Twitterで教えてもらってアップデートしました。*2
  • Jakarta EE 9.1(2021/7) サポートするJava SEの最低バージョンをJava SE 8からJava SE 11にアップデート
  • Jakarta EE 10 (2022年第一四半期にリリース予定) Jakarta EEに名前が変わってからの最初の機能アップデート

Jakarta EE 10がどうなるかについては以下のページを確認してください。

eclipse-ee4j.github.io

InfoQにもまとまった記事があるので、サマリーの日本語訳が必要な人はそちらを。

www.infoq.com

え?第一四半期ってまじかよっ!?って思ったら、リリースプランページに明記されていました。

eclipse-ee4j.github.io

Jakarta EE 10ではまだJava 11サポートするの?という件については以下で議論がされていました。Springとの温度差を感じますね。

github.com

各APサーバーのJakarta EE仕様への対応状況

2022年3月3日時点の最新版のJakarta EE(Java EE) 仕様への対応状況は以下の通りです。

Jakarta EE 8からJakarta EE 9への移行については互換性について考慮する必要がありますので、商用サーバーは避けているのが見えます。。 現実的にはJava EE 8から移行する利点はほぼないため、既存客を考慮すれば対応しないというのも理にかなってる選択となります。 が、今後Jakarta EE 10にはいつ対応するのか?の指標にはなる気がしますね。

Oracle WebLogic Server 14.1.1.0.0

Java EE 8 Jakarta EE 8 ドキュメント上はJava EE 8ですが、Jakarta EE 8のTCKが通った一覧に記載がありました。※Twitterで教えてもらいました。*3

Oracle WebLogic Serverの新機能 14.1.1.0.0

Jakarta EE Compatible Products | Enterprise Java Application and Web Servers | The Eclipse Foundation

Eclipse GlassFish 6.2.5

Jakarta EE 9.1

Eclipse GlassFish | projects.eclipse.org

Payara Enterprise 5.36.0

Jakarta EE 8

Release notes - Payara Platform Enterprise 5.36.0 :: Payara Enterprise Documentation

WebSphere Application Server traditional 9.0.5

Java EE 7

WebSphere Application Server traditional の新機能

WebSphere Application Server Liberty 22.0.0.2

Jakarta EE 9.1Jakarta EE 8 ※コメント欄で教えてもらいました。

Jakarta EE and Java EE 8 in Liberty

Open Liberty 22.0.0.2

Jakarta EE 9.1 (リファレンス実装)

Jakarta EE overview :: Open Liberty Docs

JBoss Enterprise Application Platform 7.4

Jakarta EE 8 ※Java EE 8はサポートしない感じの表を書いてるけど別ページでは同等のものですと書いている。

JBoss Enterprise Application Platform Supported Standards - Red Hat Customer Portal

WildFly 26.0.0.Final

Jakarta EE 8 (and EE 9.1 Preview)

WildFly Documentation

※実際はEE 9.1 Previewではなくて、EE 9.1のTCK(互換性確認キット)が通ってるので、EE 9.1対応。

apache-tomee-9.0.0-M7

Jakarta EE 9.1

Apache TomEE

トップページにでかでかと9.1認証されました!って書いてあって清々しい。

Interstage Application Server V13

Jakarta EE 8

https://www.fujitsu.com/jp/documents/products/software/middleware/business-middleware/interstage/products/apserver/catalog/cz1200-18.pdf

Cosminexus:uCosminexus Application Server V11

Java EE 7

構成・機能:アプリケーションサーバ uCosminexus Application Server:クラウドサービスプラットフォーム Cosminexus:ソフトウェア:日立

WebOTX v10.4

Java EE 7

WebOTX Manual | NEC

Resion 4.0.61

Java EE 6

Resin : Changes : Resin 4.0.4 Release Notes

聞かなくなったなと思ってたら死んでたか・・・・

感想

以前はGlassFishがRIでしたが、現在はLibertyがRIになっていたのが驚きました。

ちょうど別で、Red HatHibernate(Jakarta EE、JPAのRI)の実装者を新規雇用しようとしているのもtwitterで流れてきてたし、IBM系(IBM/Red Hat)*4が結構やる気なのか?という印象を持ちました。

また、GlassFishJakarta EE 10に向けて着々と歩みを進めており、OracleGlassFishを捨てたとはいえ、その先のWebLogicに向けてどうするのかというところが注目されます。

Jakarta EEは長く停滞を続けてましたが、ここからまた変わるのでしょうかねぇ?

JavaのHashMap#putAllがバグって仕様よりもメモリを多く消費していた場合があったという話。

JDKメーリングリストで投げられていましたが、面白かったので共有。

元ネタのメールはこちら。

www.mail-archive.com

HashMapとは

前提条件としてHashMapがどういう挙動をしているかがわからないと理解できないので、簡単にHashMapの説明をします。

HashMapはハッシュ表に基づいたMapの実装です。 初期値として、初期容量と負荷係数を指定できます。

どんな感じでやっているかというと、初期容量4と負荷係数0.75が指定されていたとすると、内部的には最初は

[null, null, null, null]

という内部表が生成されます。

ここにハッシュコードが7になるAがあるとします。これをA(7)と表します。これは現在の容量が4で、7 mod 4は3になるので、以下のように挿入されます。

[null, null, null, A(7)]

次にB(0)があった場合は、現在の容量が4で、0 mod 4は0になるので、以下のように挿入されます。

[B(0), null, null, A(7)]

次にC(1)は以下のように挿入されます。

[B(0), C(1), null, A(7)]

次にD(2)があった場合はどうなるでしょうか?実はそのままは挿入されません。負荷係数0.75を指定しているため、4つのうち3つまでしか使用されず、それ以上の数の要素数になった場合は内部表はリサイズされます。今回はとりあえず2倍にしておきます。その時にハッシュコードの値ですでに入っている要素の位置は変更され、以下のようになります。

[B(0), C(1), D(2), null, null, null, A(7), null]

という感じになっており、アクセスするときはハッシュ値だけわかれば高速にアクセスできるようになってます。 今回、仕様として重要なのは、内部表は実際の要素数よりも大きい数が使われており、仕様として内部表のサイズに負荷係数を掛けたものより多くの要素が入った場合に表の拡張が行われるようになっているということです。

HashMapのインスタンスには、その性能に影響を与える2つのパラメータである初期容量および負荷係数があります。 容量はハッシュ表のバケット数であり、初期容量は単純にハッシュ表が作成された時点での容量です。 負荷係数は、ハッシュ表がどの程度いっぱいになると、その容量が自動的に増加されるかの基準です。 ハッシュ表エントリ数が負荷係数と現在の容量の積を超えると、ハッシュ表のハッシュがやり直され (つまり、内部データ構造が再構築され)、ハッシュ表のバケット数は約2倍になります。

何が起こっていたか

内部表を拡張するかどうかの数値の計算が以下のようになってました。

((float)s / loadFactor) + 1.0F

正しくは以下の通りでは?とのこと。

Math.ceil((float)s / loadFactor)

最初に張り付けたメールには確認するコードが載ってます。JavaのHashMapはJavaDocによると、引数を指定しない場合内部表の初期容量は16、負荷係数は0.75になるので、要素を12個入れた場合は内部表の容量が16でなければおかしいが、putAllを使用して入れた場合は32になるという指摘でした。

よく使われてるライブラリでもコーナーケースでバグが残ってるのは面白いですね。

株式会社ビジネスブレイン太田昭和を退職しました。

これは退職エントリーです。

7年1か月勤めた会社を退職しました。 今までの職歴の中で一番長かったです。

システムインテグレーターという商売は年々難しくなっています。

一度大手Web企業にシステムインテグレーターの立場でシステムの導入に携わったことがあるのですが、ユーザー側がシステムインテグレーター側よりもITに詳しく、CI/CDやビルド及びテストの自動化等を求められましたが、何を作ればゴールなのかをシステムインテグレーター側のマネージャーが正しく把握できず、なかなか難しい状況になったことがありました。

ユーザーがシステム開発に詳しくなっており、昔はユーザーが技術的な理由でシステム開発を行えないからシステムインテグレーターに発注していたのが、ユーザーが自分でも開発することができるシステムを、自分たちで開発するよりも安く、高品質に作ってもらうためにシステムインテグレーターに発注するようになってきており、当然のように利益を圧迫する原因となっております。 また、システムインテグレーターはシステムの導入までを目的としますが、ユーザーはその先の運用までを確実に視野に入れているため、要求や要件のすり合わせが難しくなっている印象です。

その中でビジネスブレイン太田昭和では付加価値として、公認会計士が多く所属しているためユーザー企業よりも高度な業務分析を行えることを強みとしつつ、自社でシステム開発フレームワークや業務製品パッケージを作成しどのお客さんでも必要になる機能を使いまわすことによってベースとなるシステム開発コストを下げて競争力を保とうとしていました。 BPOセンターも持っており、業務自体のアウトソーシングも行っています。

私は熊本でシステム開発拠点を作って採用活動をしたり、会社全体としてのシステム開発の仕組み、ルール作りに携わったり、システム開発の自動化、ネットワーク構築、サーバー構築、システムのクラウドへの移行等幅広い範囲で経験を積めたことが非常にありがたかったと思います。 それまで良いものを作れれば良いという職人気質なところがありましたが、コストに対する意識を持てたのは良かったと思います。

その中で、自分の成長という意味で自分の知識欲を刺激されるような仕事が少なくなってきてしまっていたので退職し、新しい環境で働くことにしました。 今までと職種も違うため自分の力がどこまで通用するのか判りませんし、尖った方が多い会社なので、非常にドキドキワクワクしております。

今後ともよろしくお願いいたします。

WindowsでGraalVMを動かす。

Quarkus を触ってみようと思ってGet StartedをやってたらGraalVMが出てきたので実行してみました。 思ったよりも簡単にできましたが、Visual Studioの方でちょっとはまりどころがあったのでメモとして。

とりあえず、QuarkusのGet Startedのページについては終わった状態とします。 PowerShellが出てくるけど、特段何も困ることはありませんでした。

quarkus.io

GraalVMをインストールする。

基本的には以下のページの通りです。

quarkus.io

簡単に抜粋すると

①本家サイトからバイナリをダウンロード&解凍

www.graalvm.org

環境変数を追加

set GRAALVM_HOME=解凍した先のフォルダ
set JAVA_HOME=${GRAALVM_HOME}
set PATH=${GRAALVM_HOME}/bin;%PATH%

の2工程で終わりです。

Visual Studio 2022のインストール

Community Edition で大丈夫です。

visualstudio.microsoft.com

C++コンパイラが必要なので、C++によるデスクトップ開発を選択してインストールします。

f:id:megascus:20211208142940p:plain

GraalVMを実行する

Quarksのドキュメントだと

./mvnw package -Pnative

でビルドできるとなっていますが、Visual Studioを使用してコンパイルする場合は特殊な環境変数が必要になるので、それらが設定されたコマンドプロンプトを使用する必要があります。 スタートメニューから環境にあったコマンドプロンプトを指定して実行してください。

f:id:megascus:20211208143346p:plain

これで大丈夫なはずです。

必要に応じて、

gu install native-image

も実行してあげてください。(必要な場合はエラーメッセージに表示されてるはず)

成功したら targetの下に実行バイナリが出来ているはず。

ちなみに、環境変数が足りないと以下のようなエラーになります。スタートメニューから環境にあったコマンドプロンプトを指定して実行してください。

Error: Default native-compiler executable 'cl.exe' not found via environment variable PATH
Error: To prevent native-toolchain checking provide command-line option -H:-CheckToolchain
com.oracle.svm.core.util.UserError$UserException: Default native-compiler executable 'cl.exe' not found via environment variable PATH
To prevent native-toolchain checking provide command-line option -H:-CheckToolchain
        at com.oracle.svm.core.util.UserError.abort(UserError.java:144)
        at com.oracle.svm.hosted.c.codegen.CCompilerInvoker.addSkipCheckingInfo(CCompilerInvoker.java:104)
        at com.oracle.svm.hosted.c.codegen.CCompilerInvoker.<init>(CCompilerInvoker.java:72)
        at com.oracle.svm.hosted.c.codegen.CCompilerInvoker$WindowsCCompilerInvoker.<init>(CCompilerInvoker.java:110)
        at com.oracle.svm.hosted.c.codegen.CCompilerInvoker.create(CCompilerInvoker.java:84)
        at com.oracle.svm.hosted.NativeImageGenerator.setupNativeImage(NativeImageGenerator.java:864)
        at com.oracle.svm.hosted.NativeImageGenerator.doRun(NativeImageGenerator.java:527)
        at com.oracle.svm.hosted.NativeImageGenerator.run(NativeImageGenerator.java:488)
        at com.oracle.svm.hosted.NativeImageGeneratorRunner.buildImage(NativeImageGeneratorRunner.java:403)
        at com.oracle.svm.hosted.NativeImageGeneratorRunner.build(NativeImageGeneratorRunner.java:569)
        at com.oracle.svm.hosted.NativeImageGeneratorRunner.main(NativeImageGeneratorRunner.java:122)
        at com.oracle.svm.hosted.NativeImageGeneratorRunner$JDK9Plus.main(NativeImageGeneratorRunner.java:599)

cl.exeが見つからないといって、適当にPATHを追加すると以下のようなエラーになります。スタートメニューから環境にあったコマンドプロンプトを指定して実行してください。

Error: Error compiling query code (in C:\Users\ユーザー名\AppData\Local\Temp\SVM-15902845214491549260\JNIHeaderDirectives.c). Compiler command ''C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\bin\Hostx64\x64\cl.exe' /WX /W4 /wd4244 /wd4245 /wd4800 /wd4804 /wd4214 '-IC:\apps\graalvm-ce-java11-21.3.0\include\win32' '/FeC:\Users\ユーザー名\AppData\Local\Temp\SVM-15902845214491549260\JNIHeaderDirectives.exe' 'C:\Users\ユーザー名\AppData\Local\Temp\SVM-15902845214491549260\JNIHeaderDirectives.c'' output included error: [JNIHeaderDirectives.c, C:\Users\ユーザー名\AppData\Local\Temp\SVM-15902845214491549260\JNIHeaderDirectives.c(1): fatal error C1083: include ?t?@?C?????J???????B'stdio.h':No such file or directory]
com.oracle.svm.core.util.UserError$UserException: Error compiling query code (in C:\Users\ユーザー名\AppData\Local\Temp\SVM-15902845214491549260\JNIHeaderDirectives.c). Compiler command ''C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\bin\Hostx64\x64\cl.exe' /WX /W4 /wd4244 /wd4245 /wd4800 /wd4804 /wd4214 '-IC:\apps\graalvm-ce-java11-21.3.0\include\win32' '/FeC:\Users\ユーザー名\AppData\Local\Temp\SVM-15902845214491549260\JNIHeaderDirectives.exe' 'C:\Users\ユーザー名\AppData\Local\Temp\SVM-15902845214491549260\JNIHeaderDirectives.c'' output included error: [JNIHeaderDirectives.c, C:\Users\ユーザー名\AppData\Local\Temp\SVM-15902845214491549260\JNIHeaderDirectives.c(1): fatal error C1083: include ?t?@?C?????J???????B'stdio.h':No such file or directory]
        at com.oracle.svm.core.util.UserError.abort(UserError.java:144)
        at com.oracle.svm.hosted.c.NativeLibraries.reportErrors(NativeLibraries.java:370)
        at com.oracle.svm.hosted.NativeImageGenerator.processNativeLibraryImports(NativeImageGenerator.java:1571)
        at com.oracle.svm.hosted.NativeImageGenerator.setupNativeLibraries(NativeImageGenerator.java:1046)
        at com.oracle.svm.hosted.NativeImageGenerator.setupNativeImage(NativeImageGenerator.java:869)
        at com.oracle.svm.hosted.NativeImageGenerator.doRun(NativeImageGenerator.java:527)
        at com.oracle.svm.hosted.NativeImageGenerator.run(NativeImageGenerator.java:488)
        at com.oracle.svm.hosted.NativeImageGeneratorRunner.buildImage(NativeImageGeneratorRunner.java:403)
        at com.oracle.svm.hosted.NativeImageGeneratorRunner.build(NativeImageGeneratorRunner.java:569)
        at com.oracle.svm.hosted.NativeImageGeneratorRunner.main(NativeImageGeneratorRunner.java:122)
        at com.oracle.svm.hosted.NativeImageGeneratorRunner$JDK9Plus.main(NativeImageGeneratorRunner.java:599)

Java 14から導入されたRecordのjavadoc

見たほうがはやい。

recordであるということは明記される模様。パラメーターについては@paramで書いておけば、レコードコンポーネントとして表示される。 メソッドのドキュメントは自動生成されたものになるが、引数名のみが表示されて@paramで付けた名前が見えないのでちょっと不便かもしれない。

f:id:megascus:20211004172101p:plain
ソースコード

f:id:megascus:20211004172626p:plain
javadoc

Microsoft 365(Exchange Online)を使用してメール送信する場合に2022年10月から基本認証が使えなくなるそうです。

2022年10月からメール送信に限らず、Exchangeで基本認証が無効化されるとのこと。 メール送信(SMTP AUTH)については事前のオプトアウトはできますが、無効化された後には再有効化はできないようなので注意な。

つまり、ちまたのJavaMailを使用したメール送信のサンプルがたいてい動かなくなる。。。

SMTP AUTH以外は10月以降オプトアウトもできないし、ランダムで選ばれて勝手に無効化されるとか言ってるし、怖え。。。。 ※ランダムで選ばれて勝手に無効化されるのはオプトアウトできる

情報のソースはこちら。 techcommunity.microsoft.com