【Java】warファイル作成~Webアプリケーション起動までの簡単な流れまとめ
warファイルをつくるための簡単なメモ。
非常に簡単なwarファイル「wartest.war」というWebアプリケーションをつくる。
フォルダ構成として以下のようなかんじ。
[ルート] │ warmake.bat ....(A) │ wartest.war ....(B) │ ├─html ....(1) │ Test.html │ ├─source ....(2) │ TestServlet.java │ ├─warmake ....(3) │ ├─html │ │ Test.html │ │ │ └─WEB-INF │ │ web.xml │ │ │ ├─classes │ │ TestServlet.class │ │ │ └─lib │ j2ee.jar │ └─WEB-INF ....(4) │ web.xml │ ├─classes │ TestServlet.class │ └─lib j2ee.jar
ただこれは俺がいつもこんな感じで作っているからというだけで、
絶対に必要かと問われるとそうでもないものも多い。
というかこの中でWebアプリとして動かすうえで絶対必須なのは(4)「WEB-INF」配下だけである。
一応この手順では(4)「WEB-INF」に加えて(1)「html」配下もwarファイルとして固めるが、
上述の通り(1)「html」はWebアプリケーションとして絶対必要な資産ではない。
その他についても個人的な好みが反映されてる部分が強いが、
とりあえずこのやり方でwarファイルを作るまでを簡単に紹介する。
(1)htmlフォルダ
文字通りhtmlを格納するフォルダ。
配下に1つだけ、「Test.html」という静的HTMLを用意しておく。
これは本当にただのマジモンの静的HTMLで、これ自体に動的要素は一切ない。
役割は「サーバへデータをリクエストする」ための入り口である。
こういうのがないと、サーブレットを用意したところで、
それを呼び出すにあたってブラウザでURLを直接叩かないと呼び出せないので、
こういうのが一つあったほうが便利なのである。
そういう意味では「サーブレット実行のためのショートカット」というのが一番分かりやすい表現か。
そしてそんな話なのであれば、
ローカルのどっか適当な場所にこのHTMLファイルが存在していればよく、
warファイルに含めてWebアプリケーション化する必要はない。
そういった点を踏まえても、上述した通り、別にこれはなくても全然支障はない。
なので、これをwarにいれてるというのは個人的な好みの色が強いのだろう。
まあ、参考になれば。
ちなみに↓のようなHTMLである。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta HTTP-EQUIV='Cache-Control' CONTENT='no-cache'> <meta HTTP-EQUIV='Pragma' CONTENT='no-cache'> <meta HTTP-EQUIV='Expires' CONTENT='-1'><title>test</title>
</head>
<body>
<form action="/wartest/TestServlet" method=“post”>
テストフォームです。<br/>
以下にパラメータを入力して[送信]ボタンを押下すると、<br/>
サーバにデータを送信します。<br/>
<input type=“text” value="" id=“param” name=“param” maxlength=“2”/><br/>
<br/>
<input type=“submit” value=“送信”/>
</form>
</body>
</html>
(2)sourceフォルダ
サーブレットのソースファイルを格納するフォルダ。
リクエスト受け付けて、パラメータの値を出力するだけの非常に簡単なサーブレット「TestServlet」を用意する。
これ自体は「.java」ファイルであり、つまり「ソースコード」なので、
Webアプリケーションとして動かすためにはこれをコンパイルして「.class」ファイルを作らなくてはならない。
この「.class」ファイルは(4)「WEB-INF」配下に配置するが、後述する。
なんとなく(4)「WEB-INF」の近くがいい、という理由からここに置いているが、
(1)htmlと違ってwarファイルに含める対象ではないので、
ここに位置している意味はまったくない。
全然違うローカルのエリアで管理しても構わない。
ちなみに↓のようなjavaファイルである。
import java.io.*; import java.text.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*;public class TestServlet extends HttpServlet {
private static final DateFormat LOG_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS"); private static final String PARAMETER_STR = "param"; public TestServlet() { } public void doGet(HttpServletRequest httpservletrequest, HttpServletResponse httpservletresponse) throws IOException { doProcess(httpservletrequest, httpservletresponse); } public void doPost(HttpServletRequest httpservletrequest, HttpServletResponse httpservletresponse) throws IOException { doProcess(httpservletrequest, httpservletresponse); } private void doProcess(HttpServletRequest httpservletrequest, HttpServletResponse httpservletresponse) throws IOException { printLog("★開始"); try { Date startDate = new Date(); String param = httpservletrequest.getParameter(PARAMETER_STR); StringBuilder sb = new StringBuilder(); sb.append(LOG_FORMAT.format(startDate)); sb.append(" "); if (param != null) { if (param.equals("1")) { sb.append("MODE=1"); } else if (param.equals("2")) { sb.append("MODE=2"); } else if (param.equals("3")) { sb.append("MODE=3"); } else { sb.append("MODE=X"); } } else { sb.append("MODE=null"); } PrintWriter pw = httpservletresponse.getWriter(); pw.write(sb.toString()); pw.flush(); pw.close(); } catch(IOException ioexception) { throw ioexception; } printLog("★終了"); } private static void printLog(Object obj) { StringBuilder stringbuilder = new StringBuilder(); stringbuilder.append(LOG_FORMAT.format(new Date())); stringbuilder.append(" "); if(obj != null) stringbuilder.append(obj.toString()); System.out.println(stringbuilder.toString()); }
}
(3)warmakeフォルダ
warファイルを作るときに使うワークディレクトリ。
この配下に必要なものをかき集めて、後でwarにして固める。
(なので(1)と(4)が同じツリー構造でこの配下に出来上がっている)
詳細(A)で解説する。
(4)WEB-INFフォルダ
Webアプリケーションのうち、実際の実行に用いられる資産群。
なんか通例的に「WEB-INF」というディレクトリを使うらしいが、
これ自体は詳細省く(というか解説できん)。
ファイルとしては以下3点が格納されている。
- WEB-INF/web.xml
- WEB-INF/classes/TestServlet.class
- WEB-INF/lib/j2ee.jar
うち、2.の「WEB-INF/classes/TestServlet.class」は、
(2)sourceフォルダ配下のjavaファイルをコンパイルしたのを配置する。
1.と3.は自前で用意する。
1.にはいろいろ設定できるが、
とりあえずテスト用としてはサーブレットマッピングだけで十分である。
以下のような内容で記述して保存する。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"><web-app>
<servlet>
<servlet-name>TestServlet</servlet-name>
<servlet-class>TestServlet</servlet-class>
</servlet><servlet-mapping> <servlet-name> TestServlet </servlet-name> <url-pattern> /TestServlet </url-pattern> </servlet-mapping>
</web-app>
(A)warmake.bat
これを一発叩くと(B)warファイルが出来上がるように仕組んであるbatファイル。
この位置(ルート直下)に配置されている前提で、以下のコマンドで記述された一連の処理を実行し、
warファイル作成までを行ってくれる。
@echo offrem 1.javaソースをコンパイルして、WEB-INF\classes配下に出力する
echo Javaソースコンパイル
javac -encoding UTF-8 -classpath %JAVA_HOME%;.\source;.\WEB-INF\lib\j2ee.jar; .\source*.java -d .\WEB-INF\classes\rem 2.warmake自体とその配下を全部消して作り直し
echo warmake全削除と空フォルダ作り直し
rmdir /s /q .\warmake
mkdir warmakerem 3.必要なものだけwarmake配下にコピーする
echo 必要な資産をwarmake配下にコピー
xcopy /y /s /e /i /q html warmake\html
xcopy /y /s /e /i /q WEB-INF warmake\WEB-INFrem 4.jarコマンドでwarファイルをつくる
jar -Mcf wartest.war -C warmake/ .pause
まず1.で、(2)sourceフォルダにあるjavaファイルをコンパイルし、(4)WEB-INFのclassesフォルダに出力する。
次に2.で、war作成用の作業用ディレクトリ(3)warmakeを根こそぎ削除し、空フォルダとして作り直す。
次に3.で、(1)htmlと(4)WEB-INFを(3)warmake配下にディレクトリツリー構造含め丸ごとコピーしてくる。
最後4.で、jarコマンドを使ってwarファイルに固めて、作成された(B)warファイルを同階層に出力する。
ちょっとググればすぐ出てくるが、
warファイルの実態はただのzipファイルなので、
jarを作るのと同じやり方で作成することができる。
違うやり方でも作成できるのだが、一番手っ取り早いのはjarコマンドで固めてしまうことである。
その際、デフォルトでくっつくマニフェスト(META-INF)を除くため、「-M」オプションを付与するが、
これはあまり重要ではない(別にマニフェストがあっても支障はない)
今回のような小規模なアプリでは、(2)sourceフォルダ配下は1ファイルしかなかったが、
普通のWebアプリでソース1ファイルなんてことはまず有り得なく、
実際には数百数千のソースコードを相手にコンパイルが必要になる。
この場合、パッケージ構造等を加味しても、
↑のbatファイルで記述しているコンパイルのコマンド(1.の部分)では大分無理があることは必至で、
何らかの方法を考えなくてはならない。
その対策として、build.xmlを用意してantに食わせるやり方がよく知られているが、ここでは割愛する。
まあとにもかくにも、こんな感じで出来上がった(B)wartest.war、
jarコマンドで作ったこともあって、tオプションで中身が見れる。
実際中身を見てみると↓のような感じに仕上がっている。
0 Fri Jan 26 19:05:52 JST 2018 html/ 763 Fri Jan 26 18:16:18 JST 2018 html/Test.html 0 Fri Jan 26 19:05:52 JST 2018 WEB-INF/ 0 Fri Jan 26 19:05:52 JST 2018 WEB-INF/classes/ 2228 Fri Jan 26 19:05:52 JST 2018 WEB-INF/classes/TestServlet.class 0 Fri Jan 26 19:05:52 JST 2018 WEB-INF/lib/ 1726123 Thu Jan 15 16:21:46 JST 2015 WEB-INF/lib/j2ee.jar 458 Fri Jan 26 16:18:12 JST 2018 WEB-INF/web.xml
これにて、必要なものだけが詰まったWebアプリケーションアーカイブが完成したのだ!
こうして出来上がった(B)wartest.warを、
tomcatのwebapps配下にコピーしてtomcat起動すれば、
あとは勝手にWebアプリケーションとして展開してくれる。楽ちん。
一つ気を付けなくてはならないのは、この後tomca起動すると、
以下のように、「wartest.war」が解凍されたフォルダが直下に1つ出来上がる。
このフォルダがあると以後はそっちが優先して読み込まれてWebアプリケーションが立ち上がる。
なので、例えば「TestServlet.javaを修正して再コンパイルしたので、もう一度tomca再起動!」ってやりたい場合、
↑の手順に従ってwarファイルを再作成してwebapps配下のwarファイルを書き換えても、
このwebapps配下にある「wartest」フォルダを最新化しない限り修正は反映されない。
もし修正を反映させるなら、webapps配下の該当フォルダ自体を丸ごと削除して、warファイルを上書きして再起動するのだ。
(そうすると修正後のwarファイルを解凍してから起動してくれるので、修正内容が反映される)
で、無事にtomcatが立ち上がったら、
ブラウザを立ち上げてURLに「http://localhost:8080/wartest/html/Test.html」と入力してみる
テキストボックスに適当な値を入力して「送信」ボタンをクリックすると、
サーバ側の「TestServlet」が呼び出されて、レスポンスが表示される。
基本的にはこの流れでwarファイル作成とWebアプリケーション配備が可能である。
↑で述べたとおり、
現実的に運用されている/稼働しているWebアプリケーションのほとんどは、
大小さまざまなJavaクラスで構成されており、
実際のところこんな簡単なWebアプリケーションなど存在しない。
どちらかというと「ちょっとした検証」のために用いる、
という手法のための意味合いが強いと思う。
(個人的にもその用途である)
Webアプリケーション全体を用意するのは骨がおれるので、
検証したい箇所だけに限定した簡単なサーブレットを用意してテストする、というような目的。
その点では、このやり方でのWebアプリケーション作成とテスト方法は、
個人的には割と重宝している。
COMMENT:
kooさん>
返信に遅くなってしまい申し訳ありません、コメントありがとうございます。
そしてお役に立てたなら良かったです。
そうですね、この記事自体は若干古い環境で作ってるので、今はもうj2ee.jar単品では手に入らないかもしれません。javax.servlet.*が入ってるならservlet-api.jarでも問題ないと思います。
COMMENT:
AUTHOR: koo
DB接続確認のアプリを作るときとても参考になりました。文中のj2ee.jarはネットで見つけられず、Tomcat標準のservlet-api.jarで代替できました。