rm /blog

IT系技術職のおっさんがIT技術とかライブとか日常とか雑多に語るブログです。* 本ブログに書かれている内容は個人の意見・感想であり、特定の組織に属するものではありません。/All opinions are my own.*

【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点が格納されている。

  1. WEB-INF/web.xml
  2. WEB-INF/classes/TestServlet.class
  3. 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 off

rem 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 warmake

rem 3.必要なものだけwarmake配下にコピーする
echo 必要な資産をwarmake配下にコピー
xcopy /y /s /e /i /q html warmake\html
xcopy /y /s /e /i /q WEB-INF warmake\WEB-INF

rem 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アプリケーションとして展開してくれる。楽ちん。

 

20180126_neta_webapps.jpg

一つ気を付けなくてはならないのは、この後tomca起動すると、
以下のように、「wartest.war」が解凍されたフォルダが直下に1つ出来上がる。

 

20180126_neta_webapps2.jpg

このフォルダがあると以後はそっちが優先して読み込まれてWebアプリケーションが立ち上がる。
なので、例えば「TestServlet.javaを修正して再コンパイルしたので、もう一度tomca再起動!」ってやりたい場合、
↑の手順に従ってwarファイルを再作成してwebapps配下のwarファイルを書き換えても、
このwebapps配下にある「wartest」フォルダを最新化しない限り修正は反映されない。
もし修正を反映させるなら、webapps配下の該当フォルダ自体を丸ごと削除して、warファイルを上書きして再起動するのだ。
(そうすると修正後のwarファイルを解凍してから起動してくれるので、修正内容が反映される)




で、無事にtomcatが立ち上がったら、
ブラウザを立ち上げてURLに「http://localhost:8080/wartest/html/Test.html」と入力してみる

 

20180126_neta_browser1.jpg

テキストボックスに適当な値を入力して「送信」ボタンをクリックすると、
サーバ側の「TestServlet」が呼び出されて、レスポンスが表示される。

 

20180126_neta_browser2.jpg

基本的にはこの流れでwarファイル作成とWebアプリケーション配備が可能である。




↑で述べたとおり、
現実的に運用されている/稼働しているWebアプリケーションのほとんどは、
大小さまざまなJavaクラスで構成されており、
実際のところこんな簡単なWebアプリケーションなど存在しない。
どちらかというと「ちょっとした検証」のために用いる、
という手法のための意味合いが強いと思う。
(個人的にもその用途である)
Webアプリケーション全体を用意するのは骨がおれるので、
検証したい箇所だけに限定した簡単なサーブレットを用意してテストする、というような目的。
その点では、このやり方でのWebアプリケーション作成とテスト方法は、
個人的には割と重宝している。