今は昔、Java1.4の時代につくられ、今も現役のTomcatを利用したWebアプリがあります。
そんなWebアプリの開発環境に、ひょんなことからEmbed Tomcatを導入した時のお話しです。
導入についての細かな設定情報などは各所に散らばっているので、ここではその経緯などを読み物的にさらっと。。。
前提条件
- Eclipseのサーバ機能でTomcatを立ち上げて動かしていた
- DBへの接続はJNDIのNamingコンテキストを利用
そもそも何が起きたのか
環境構築時にEclipseのJavaプロジェクトが正常に動的サーバプロジェクトとして認識されなかった
アプリケーションやサーバ設定全て含めてEclipseのプロジェクト形式でGitに格納しており
基本的にはサーバ機能のTomcat導入まで含めてプロジェクトのインポートでほぼ環境構築は終了出来るはずでした。
(今までのメンバーはそれで大丈夫だった)
それでは、今までの開発メンバーと何が違ったのかというと。。。
開発マシンがMac!
なんてことでしょう。
たかだか、Macに環境が変わった時点で動かないようなプロジェクト構成だったとは!!
環境依存しない方式への移行を検討
とりあえず下記の理由により原因追及は早々にあきらめた
- 自分の環境がWindowsなのでこちらで再現させられない
- 前から再起動などサーバ機能を利用することの面倒さを感じていた
アプリケーション全体の移行。。。したいけど
最初に記載した通り、古き時代からのWebアプリでありフレームワークも独自なため、
いっそのことSpring(Boot)に移行してしまえば済むのになどと妄想をいただきつつ
でも、現行世代のフレームワークを参考にする、事項のような方式を検討するに至りました。
Embed Tomcatに変更してしまえ
Javaのうまみといえば、バイトコードにしてしまえば環境依存しない!
もちろんTomcatだって例外ではないので、OSもしくはIDEになるべく依存しないように
Embed Tomcatへの移行を心に誓ったのでありました。
移行時にやった作業
必要なjarの導入
必要なjarは全て手動でビルドパスにぶっこむ、依存関係もへったくれもないプロジェクト形式なので
下記のjarをダウンロードしてきて、ビルドパスに追加します。
- tomcat-embed-core-8.5.xx.jar
- tomcat-servlet-api-8.5.xx.jar
- tomcat-dbcp-8.5.xx.jar
TomcatのランチャーMainメソッド作成
public static void main(String[] args) throws Exception {
Tomcat tomcat = new Tomcat();
StandardContext ctx = (StandardContext) tomcat.addWebapp("/hogecontext",
Paths.get("WebContent/").toAbsolutePath().toString());
WebResourceRoot resources = new StandardRoot(ctx);
DirResourceSet dirSet = new DirResourceSet();
dirSet.setBase(Paths.get("build/classes").toAbsolutePath().toString());
dirSet.setWebAppMount("/WEB-INF/classes");
resources.addPreResources(dirSet);
ctx.setResources(resources);
tomcat.getService().addConnector(createAJPConnector());
ctx.getNamingResources().addResource(createDBContext());
tomcat.start();
tomcat.getServer().await();
}
static Connector createAJPConnector() {
Connector ajpConnector = new Connector("AJP/1.3");
ajpConnector.setPort(8009);
return ajpConnector;
}
static ContextResource createDBContext() {
ContextResource jdbcResource = new ContextResource();
jdbcResource.setName("jdbc/hogedb");
jdbcResource.setType(DataSource.class.getName());
jdbcResource.setAuth("Container");
jdbcResource.setProperty("factory", BasicDataSourceFactory.class.getName());
jdbcResource.setProperty("driverClassName", org.postgresql.Driver.class.getName());
jdbcResource.setProperty("url", "jdbc:postgresql://192.168.33.10:5432/hogedb");
jdbcResource.setProperty("username", "user");
jdbcResource.setProperty("password", "password");
return jdbcResource;
}
細かい工程の説明は省略しますが、コンパイルされたclassesディレクトリが、Embed TomcatのWEB-INF配下にあるように見せるために「WebResourceRoot#addPreResources」が必要です。
Tomcatの起動
実行→Javaアプリケーションで起動すると、web.xmlに定義されている「load-on-startup」が順次実行されているのが確認出来た。
これはイケルって思ったのもつかの間、エラー発生!
javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
調べてみると、JNDIの初期位置「java:comp/env」自体が定義されていないようです。
APIリファレンスでは、下記のように書いてあったので、Embedでは手動で有効化しないといけないようでした。(もしくは、通常のサーバ機能ではどこかに有効化の設定が入っている?そこまでは調査しきれていないです、すみません)
Enables JNDI naming which is disabled by default.
そのため、下記の1行を起動メソッドに追記しました。
tomcat.enableNaming();
改めてTomcatの起動
コンソール上に下記のような表示が出ました。
12 11, 2017 12:47:48 午前 org.apache.coyote.AbstractProtocol start
情報: Starting ProtocolHandler ["ajp-nio-8009"]
どうやら、正常に起動出来たようです。AJP経由での通信で正常なアプリケーションの動作も確認できました。
動作させてみての所感
Eclipseサーバ機能から起動のTomcatより断然早いです。どこのオーバーヘッドが原因なのか調査していませんが、体感で分かるくらいもっさり感が解消しています。これはラッキーでした。
実際にMacで動作するかどうか
実はまだ、Macで実際に試してはいないのですが(この投稿の方が日時的に先にきてしまったので。。。)
Javaの特性上大丈夫だとは思っています。
後日結果報告します!!きっと大丈夫!
→ 何事もなくちゃんと動きました!!!
残作業
ビルド用にgradleタスクは作成してあるのですが、Tomcat起動のタスクも追加したいですね。
最後に
環境依存するようなも設定を一つでも排除できた事は喜ばしいことだと思っています。
ただただ、今更感がどうしても拭えない。
早く、モダンなフレームワークに移行したい!!
追記
明日は @yam0918 さんです。