Tomcat 魔改造
Tomcat で勘違いされていることの一つに 「Java EE 対応サーバである」というのがあると思う。
Tomcat 自体はあくまで「サーブレットコンテナ」なのだが、実際に利用する際には html などの静的なファイルも出力できないと使い物にならないためウェブサーバー機能は当然持っている。
さらに、魔法使いのような人たちが tomcat を魔改造して Jave EE(現在のJakarta EE。併記するのもアレなので以下 Java EE と表記) の機能を付け加えて使っているため、この手の誤解が生まれたと思われる。
最近だと私の周囲の上の方々がノーマル Tomcat に hibernate を使えるようにして、これを基盤に REST API を実装してしまった。
すげ…
Tomcat 自体には手を入れてないので、「そんなにすごくないんじゃない」と言われる方もいるかもしれないが、これやった人たちはいわゆる Java 特化型のプログラマーでないからね。
というか専業のプログラマーでもないっていう。。。
一体、どういう経験を積めばそんなことがいとも簡単にできるようになるのかワイには全然見当もつかなかったのだが、横で見ていて気がついたのは、技術の把握の仕方がとにかく速いってこと。
将来的にはあのレベルまで到達したいし、そうなるコツの一端には触れた気分にはなった。
が、同時に、いきなりあのレベルに到達するのは無理だというのも思い知らされましたね。
では、どうするか?
TomEE
幸い、私には時間もあることだし、別のアプローチを考えてみた。
そしたら、ありましたよ、興味深いプロジェクトが。
あまり引っ張ってもしょうがないので、ここら辺で本題に入ると TomEE という Tomcat に Java EE の仕様を組み込んだプロジェクトが存在した。
Tomcat + Java EE で TomEE という直球なネーミング。なお TomEE は tommy (トミー?)と発音するらしい。
公式サイトはこちら。
これなら、魔法を使わなくても Tomcat ライクな Java EE 環境が手に入る!
TomEE WebProfile, TomEE Plus, TomEE MicroProfile, TomEE Plume
ひと口に Tomcat + Java EE と言っても Java EE の仕様の何を組み込むかによって、いくつかのプロダクトが存在しうる。
実際、TomEE WebProfile, TomEE Plus, TomEE MicroProfile, TomEE Plume という機能の異なるバージョンがリリースされている。
公式サイトにわかりやすい図があったので持ってくると以下の通り。
https://tomee.apache.org/comparison.html より
Tomcat を使う場合、ノーマルでは実装されてなくて何かと不便な EJB や JAX-RS は TomEE プロダクツ群には全て入っている。とても心強い。
逆に、若干、心配なのは、日本語で TomEE の記事がほとんどないこと。
成熟度や信頼性に問題あるのか???
TomEE Plus の導入
というわけで TomEE を試してみたい。
MacOS の場合、ありがたいことに、homebrew 経由で TomEE Plus を入れることができる。
brew install tomee-plus
でインストールが始まる。
現在(2022夏)だと TomEE 8.0 系列が入るようだ。Jakarta EE 9 に対応した 9.0 系列を導入したい人は、homebrew を使わずに公式サイトからパッケージを落としてみてください。
試しに使ってみる程度なら、Java EE 8/Jakarta EE 8 のどちらにも対応している 8.0 系列で十分だと思いますが。
intel Mac では /usr/local/Cellar/tomee-plus/ 以下に諸々のファイルが展開される。
/usr/local/Cellar/tomee-plus/(version)/libexec/bin に startup.sh というシェルスクリプトがあるので、これを実行する。
./startup.sh
この時点で http://localhost:8080 でマネージャーが立ち上がるのだが、ユーザー登録がまだなので登録しておこう。
/usr/local/Cellar/tomee-plus/(version)/libexec/conf/tomcat-users.xml の
<role rolename="tomee-admin" />
<user username="hoge" password="xxxxxx" roles="tomee-admin,manager-gui" />
の username と password を適当に設定。
これらを使ってログインすると
Tomcat でもお馴染みの GUI な管理画面が表示される。
なお、TomEE 自体を終了させたければコマンドライン から
./shutdown.sh
を実行する。
当然だが、とってもトムキャっぽい。
サンプルの実行
サンプルの一覧が https://tomee.apache.org/tomee-8.0/examples/ にある。
実際のソースコードは
GitHub: https://github.com/apache/tomee/tree/main/examples
で公開されている。
このうちノーマル雄猫(tomcat)では扱えない JPA 機能を使った jpa-hibernate プロジェクトに興味を持って動かしたのだが、test プログラムが走るのみでちょっと物足りない。
しかし、簡潔なユニットテストのコーディングスタイルにちょっと「おお!」となったので、この改変を試みたい。
ノーマル雄猫でも hibernate.cfg.xml などをうまく設定すると hibernate 自体は使えるんですが(『tomcat FAQ』で解説されています)、やり方がやや面倒で慣れてない人だと難しいと思います。
サンプルプログラム試作
TomEE 自体は Tomcat をベースにつくられているので、EE の機能をがっつり取り込んだプロジェクトでもない限り war に固めて上記の管理画面からデプロイさせれば普通に動きます。
ハロワ
私は NetBeans 使っているんですが、デフォルトの Web App のプロジェクトを作成してデプロイするとしっかりハローワールドしてくれました。
ハロワールドな index.html をブラウザからのリクエストに応じて出力させているだけですがw
サーブレット(EJB も少々)
続いてサーブレットコンテナ機能のチェック。
非同期処理のサンプルである async-servlet というプロジェクトがあったので、これを改変。
オリジナルは TomEE 9 向けに書かれていたので、TomEE 8 向けに少々手直し。
また、サンプルはそのままビルドするとライブラリ化して開発マシンのローカルリポジトリに収納させる仕様になっているようで、この点も .war ファイルを吐き出してくれるように改変。
(追記)ソースコードは air-h-128k-il 氏のリポジトリに置かしてもらいました。
GitHub: https://github.com/air-h-128k-il/async-servlet2
まず、適当に作成した index.html を表示。
ここは問題ないでしょう。
リンクを踏むと http://localhost:8080/async-servlet2/calc に飛ぶ。
これがサーブレットで書かれているので、表示できればOKなんですが。。。
お、表示された(↓)。
このサーブレットは、GET リクエストのパラメータ(クエリ文字列)を受け取って四則演算の結果を返してくれるんですが、パラメータがないと上の画面にあるように使い方を表示してくれます。
このサーブレットの面白いところは非同期処理を使っているところ。
リクエストを受け取ってから結果を表示させるまでの遅延時間やタイムアウト時間が設定できます。
実際、Example にあるように
http://localhost:8080/async-servlet2/calc?x=2&y=4&op=multiply&async=true&delay=1000
にアクセスすると、やや遅れて(正確には 1000ms=1sec なんでしょうけど)2 x 4 の結果の 8 が表示されます。
なお、サーブレットの非同期処理は servlet 3.0 から導入された機能なので、サーブレットはノーマル雄猫の 8 でも動くはずです。
これ自体は Java EE の仕様とは認知されていないと思います。
詳しくは『Servlet標準の非同期処理に触れてみる』などを参照してください。
なのですが、実は、ここで Java EE 要素が軽く出てきています。
このサーブレットは、以下のように CalcBean.java を EJB として扱っています。
CalcServlet.java
@EJB
private CalcBean bean;
EJB は、Java EE の仕様なのでこの部分に関してはノーマル Tomcat では動かないと思います。具体的な挙動は『EJB の話』に書いてます。
徐々に Java EE らしさが出てきたでしょうか。
もうちょっと苦労するかと思いましたが、あっさり動きましたね。
TomEE なかなかやる?
いよいよ JPA hibernate
サンプルの素にしようとした jpa-hibernate ですが、pom.xml をよくみると openejb-core-hibernate が使われている。。。
これ、純正 hibernate ではなく、あくまでユニットテスト用の hibernate ですよね。
さらに、ejb-jar に相当すると思われる機能も使っている。。。
インターセプターとかあそこらへん苦手なんですが。
これは、簡略化して一からプロジェクト作成ですね。
結局、諸々調整して、シンプルな構成のオブジェクトを永続化させるサーブレットに仕立て上げた。
ソースコードの主要な部分は以下の通り。
Movie2.java
@Entity
@Table(name = "movie2")
public class Movie2 implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String director;
private String title;
(以下略)
Movies.java
@Stateful
public class Movies {
@PersistenceContext(unitName = "movie-unit", type = PersistenceContextType.TRANSACTION)
private EntityManager entityManager;
@Inject
private Movie2 movie;
public void addMovie(Movie2 movie) throws Exception {
entityManager.persist(movie);
}
(他にも関数は作成したが、実際使っているのはこれだけなので以下割愛)
実際の動作としては、まず、永続化させたい文字列をブラウザ上で指定。
フォームを使って、セットした文字列を GET でサーブレットに投げる。
サーブレットは、Movies の EntityManager を利用してこれらを永続化。
実際、PostgreSQL のテーブルをのぞいてみると
テーブル自体を作成して、ブラウザ上で指定した文字列を該当カラムに記録していますね。
いやあ、JPA も利用できるものなのですね。
ちょっと設定でまごつきましたが(笑)
あと、面白いのは、データソースは JTA で指定してありますが、
情報 [http-nio-80-exec-233] org.apache.openejb.config.AutoConfig.setJtaDataSource Adjusting PersistenceUnit movie-unit <jta-data-source>
to Resource ID 'jpa-hibernate5/jdbc/moviedb' from 'jdbc/moviedb'
情報 [http-nio-80-exec-233] org.apache.openejb.config.AutoConfig.setNonJtaDataSource Adjusting PersistenceUnit movie-unit <non-jta-data-source>
to Resource ID 'jpa-hibernate5/jdbc/moviedbNonJta' from 'null'
と nonJTA のデータソースも自動で作っている点でしょうか。
内部的にどういうふうに利用しているのかわかりませんが。
JAX-RS で REST
ここまできたら、REST なウェブサービスも作ってみたい(笑)。
シンプルにこんなコードを書いた。
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
@Path("/hello")
public class Hello {
@GET
public String responseGet() {
return "Hello GET";
}
@POST
public String responsePost(String message) {
return message;
}
}
これを TomEE にデプロイしてコマンドラインから curl で API を叩く。
GET:
% curl http://localhost/hogerest/hello
Hello GET
POST:
% curl -X POST -d 'hello post' http://localhost/hogerest/hello
hello post
プロジェクトに取り込むライブラリの中に jakartaee-api が含まれていれば、javax.ws.rs.Path などは見つかるはずなのでビルド自体はできる。
ただ Tomcat にデプロイした場合は JAX-RS は実装されてないので、上記のコマンドを叩いても 404 が返ってくるはずだ。
TomEE けっこう使えるのでは?
TomEE の使い所
思っていた以上に使えるというのがざっと試してみた上での感想。
特に EJB や JAX-RS などの機能を簡単に使用したい場合、特別な工夫なしで使えるので便利だと思った。
Java でウェブアプリを作成する際、デプロイするコンテナやアプリケーションサーバのことを気にかけておかないと、後でとんでもないことになるが(要するに Tomcat か WildFly かなりを予め決めておかないと移行作業が大変になる)、ここら辺あまり意識しなくてもいい感じがする。
これは、プロジェクトの初期段階でコーディングする際のかなり大きなメリットでしょう。
ただし、JavaEE の機能をガシガシ使うようなプロジェクトの場合、TomEE で本当にイケるのかというと(そこまで使い込んでないので)正直よくわからない。
そもそも、そこまでガシガシ使うなら、最初から WildFly や GlassFish 選べばいいという気がしないでもない。
だから、サーブレットや JSP の使用多め、けど、JPA 経由で hibernate などを軽めに使いたいなんてプロジェクトには向いているかもしれない。
これにしても Spring 系でいいじゃないか?という声も聞こえてきそうだが、ワイは Spring 系まともに使ったことないのでよくわかんなんだよね。そういうわけで比較できません(笑)。
Java 自体、そんなに使い込んでいるわけじゃないしね。
ただ、本格アプリケーションサーバや Spring などとの競合というのは、当たっているかもしれないなと思うところはある。
というのは TomEE 自体の完成度の割にコミュニティの規模が小さいと感じるから。
日本語で書かれた情報って本当に少ないでしょ?
Spring 関連の情報なんてググれば山ほど出てくるのに。
TomEE 本家のフォーラムも覗いてきたが、トラフィックはやはり少ない。
かなり強引にまとめ
Tomcat に慣れていて、JavaEE の機能をお手軽に追加したいって人には向いていると思います。
魔法のような特殊設定しなくても JavaEE の主要な機能が使えます(笑)。