#navi_header|Java| MavenプロジェクトでのServletプログラミングで、Eclipse上で「サクサク」、つまりJavaソース修正→Eclipse上で保存→Eclipseによる自動コンパイル→即座にServletContainer上に反映という素早い開発サイクルを実現するためのメモです。 対象:フルスペックのJ2EEサーバ機能を用いない、TomcatやJettyなどの軽量ServletContainer上で動作するWebアプリケーション。 環境:Maven3, Eclipse 3.6 or 3.7 以上, m2e プラグイン, Tomcat 5 以上 #more|| #outline|| ---- * Java Servlet プログラミングにおける「サクサク開発」の基本方針 Java Servlet プログラミングで何がプログラマを苛立たせるか。それは "deploy" 作業であると思います。Java Servlet プログラミングで根強い人気を誇る Sysdeo Eclipse Tomcat Launcher Plugin は、Plugin側でTomcatを起動して、Plugin側でEclipseプロジェクトにあわせてContextを自動設定してくれます。これによりプログラマは "deploy" について一切気にする必要が無くなり、Eclipse上でJavaソースを修正→即座にTomcatがContextをリロード→ブラウザ上から動作確認することが出来ます。また、Seasar2 による SAStruts フレームワークではhot deploy機能により、classファイルの更新を検知して自動的に最新のclassファイルをロードしてくれます。これによりプログラマは修正する度に "deploy" する必要は無くなります。 ここまでであれば特に難しい問題はありません。Eclipseプロジェクトの出力ディレクトリを"WEB-INF/classes/"に設定し、Servlet Container側で適切にContextのドキュメントベースを設定し、最後にServlet Container側でclassファイルが更新されれば自動的にContextをリロードする設定して準備完了です。 問題が出てくるのはMavenプロジェクトの場合で、WEB-INFを含めたWebコンテンツは "src/main/webapp/" 以下に、ビルドされたclassファイルは "target/classes/" 以下に、と分断されてしまいます。そのままですとどうしても一度warまでパッケージングした後、ServletContainerにdeployさせる必要が出てきます。 この問題を解決するためには jetty-maven-plugin や tomcat-maven-plugin を導入します。これらを使うことで、"src/main/webapp/" と "target/classes/" の分断をplugin側で適切にハンドリングしてそれぞれのServlet Containerをデバッグ起動することが可能となります。 ここまでで、"deploy" を省略するためのアプローチを以下に整理します。 : Sysdeo Eclipse Tomcat Launcher Plugin :#block||> - http://www.eclipsetotale.com/tomcatPlugin.html - Eclipse上からTomcatをデバッグモードで起動し、Java Servletのデバッグを可能にする。 - 老舗プラグインで使いやすさもあります。ただし、Mavenプロジェクト(m2eプラグイン)と組み合わせるには "DevLoader" をTomcat側にセットアップする必要があるようです。 - 参考:DevLoaderとMaven/m2e連携時のトラブルなど -- http://www.eclipsetotale.com/tomcatPlugin/readmeDevLoader.html -- http://bagineer.blog59.fc2.com/blog-entry-98.html -- http://d.hatena.ne.jp/Ewigkeit/20071122/1195658893 ||< : SAStruts (Seasar 2.4 SMART deploy) :#block||> - http://sastruts.seasar.org/ - StrutsのXML地獄から開発者を解放するSAStruts (1/3) - @IT -- http://www.atmarkit.co.jp/fjava/rensai4/saweb02/saweb02_1.html - crossroad's Blog Eclipse + Maven2 + Tomcat + Seasar2 の開発環境ベスト!?プラクティス -- http://bagineer.blog59.fc2.com/blog-entry-98.html ||< : jetty-maven-plugin :#block||> Jettyは軽量のJava Servlet Containerです。jetty-maven-pluginをMavenプロジェクトに組み込むことで、"mvn jetty:run" でMavenのディレクトリ構成に従ってServletContainerが起動します。これにより、Mavenでビルド or m2eプラグイン導入済みのEclipse側で自動コンパイルされたclassがJetty側でも間を置かずに自動リロードしてくれるようになります。 - Jetty 7 or 8以降 -- http://wiki.eclipse.org/Jetty/Feature/Jetty_Maven_Plugin - Jetty 6以前 ("maven-jetty-plugin"という名前だった) -- http://docs.codehaus.org/display/JETTY/Maven+Jetty+Plugin ||< : tomcat-maven-plugin :#block||> Mavenのplugin内部でTomcatをLaunch可能にします。当初は codehaus で開発されていましたが、バージョン 2.0 以降は Tomcat プロジェクト側に開発が移ったようです。 - 2.0以降:2.0-SNAPSHOTは開発中、Maven Central Repositry に登録されているのは 2.0-beta-1 までです。 -- http://tomcat.apache.org/maven-plugin.html -- http://tomcat.apache.org/maven-plugin-2/ -- http://tomcat.apache.org/maven-plugin-2.0-SNAPSHOT/ - 1.0, 1.1, 1.2:codehausのドキュメント参照。 -- http://mojo.codehaus.org/tomcat-maven-plugin/index.html - 5. Maven による実アプリケーション開発 | TECHSCORE(テックスコア) -- http://www.techscore.com/tech/Java/ApacheJakarta/Maven/5/ 以下注意点です。 - GroupId : 1.2 までは "org.codehaus.mojo", 2.0 以降は "org.apache.tomcat.maven" に変更されています。 - どのリポジトリを使えば良いのか? -- 1.1 と 2.0 については Central(repo1) に登録されているのを使えば良いと思います。 -- 1.2 については codehaus リポジトリにのみ登録されているため、1.2を使う場合は settings.xml か pom.xml にcodehausのリポジトリ設定を追加する必要があります。 - 利用するTomcatのバージョンを選択できるか? -- 2.0以降は Tomcat6 : tomcat6-maven-plugin, Tomcat7 : tomcat7-maven-plugin というようにTomcatのバージョンごとに分かれているようです。 -- Tomcat Maven Plugin - Adjust Tomcat Version --- http://mojo.codehaus.org/tomcat-maven-plugin/examples/adjust-embedded-tomcat-version.html --- http://tomcat.apache.org/maven-plugin-2/tomcat6-maven-plugin/examples/adjust-embedded-tomcat-version.html ||< 本記事では、以下の理由から バージョン 1.2 のtomcat-maven-plugin を使ってみます。 - Jetty版については公式サイトにかなり詳しく色々なバリエーションと応用技法が載っているのに対し、Tomcat版はあまり情報が豊富でない気がした。 - たまたま本記事を書くきっかけとなったEclipseプロジェクトの本番deploy環境がTomcatだったため、環境を合せておきたかった。実働環境はTomcatなのに、開発をJettyとすることで不要な地雷を踏み抜きそうな悪寒がした。 - pom.xmlにリポジトリ設定を記述する練習をしたかったので、あえてcodhausのsnapshotリポジトリの追記が必要な 1.2 を選択。 * サンプルコード 環境: MacOS X 10.7.3 $ mvn -version Apache Maven 3.0.3 (r1075438; 2011-03-01 02:31:09+0900) Maven home: /usr/share/maven Java version: 1.6.0_31, vendor: Apple Inc. Java home: /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home Default locale: ja_JP, platform encoding: SJIS OS name: "mac os x", version: "10.7.3", arch: "x86_64", family: "mac" Eclipse 3.7 Java EE M2E - Maven Integration for Eclipse 1.0 archetypeとしてcodehausのwebapp-jee5を使って "Hello, World" のServletサンプルコードを作成します。 #pre||> $ mvn archetype:generate … Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : org.codehaus.mojo.archetypes:webapp-jee5 Choose archetype: 1: remote -> org.codehaus.mojo.archetypes:webapp-jee5 (-) Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : 1 Choose version: 1: 1.0 2: 1.0.1 3: 1.1 4: 1.2 5: 1.3 Choose a number: 5: Define value for property 'groupId': : test Define value for property 'artifactId': : servlet1 Define value for property 'version': 1.0-SNAPSHOT: : Define value for property 'package': test: : test.servlet1 Confirm properties configuration: groupId: test artifactId: servlet1 version: 1.0-SNAPSHOT package: test.servlet1 … $ cd servlet1/ $ find . . ./pom.xml ./src ./src/main ./src/main/java ./src/main/java/test ./src/main/java/test/servlet1 ./src/main/webapp ./src/main/webapp/index.jsp ./src/main/webapp/WEB-INF ./src/main/webapp/WEB-INF/web.xml ||< とりあえずEclipseにインポートし、以下のJavaクラスを追加します。 src/main/java/test/servlet1/Hello.java: #code|java|> package test.servlet1; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class Hello extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html"); PrintWriter out = resp.getWriter(); out.println(""); out.println("
"); out.println("Hello, World!"); out.println(""); out.println(""); out.close(); } } ||< src/main/webapp/WEB-INF/web.xml に以下のようにservlet設定を追加します。 #pre||>