Gradle warプロジェクト作成¶
はじめに¶
Gradleにおいて、warプラグインを適用し、サーブレットアプリケーションをビルドするGradleプロジェクトを作成します。
ただし、gradleのinitタスクには war 用のオプションが存在しないので、別な手段で作成します。
手順¶
単一モジュールのwar作成プロジェクトをいちから作成¶
準備¶
- プロジェクトのディレクトリを用意します。
work % mkdir helloWar work % cd helloWar helloWar %
- settings.gradle.kts ファイルを新規に作成し、プロジェクト名を記述します。
rootProject.name = "helloWar"
この時点でのプロジェクトディレクトリ
helloWar +-- settings.gradle.kts
- wrapperを実行
helloWar % gradle wrapper :
この時点のディレクトリ
helloWar +-- .gradle +-- gradle | +-- wrapper +-- gradlew +-- gradlew.bat +-- settings.gradle.kts
build.gradle.kts の編集¶
build.gradle.ktsを作成し、warプラグインを使うビルドの定義を記述していきます。
plugins {
war
}
この時点のディレクトリ
helloWar +-- .gradle +-- build.gradle.kts +-- gradle | +-- wrapper +-- gradlew +-- gradlew.bat +-- settings.gradle.kts
ソースファイルやWeb定義ファイルがないですが、ビルドが可能です。
helloWar % gradle wrapper :
ビルド結果が、buildディレクトリ以下に生成されます。warファイル名は、プロジェクト名に.warを付けた名前です。
helloWar +-- build +-- libs | +-- helloWar.war +-- tmp
helloWar.warの中は、MANIFEST.MFだけが入った状態です。
- サーブレットAPIのライブラリを指定
Mavenリポジトリから、サーブレットAPIを取得しコンパイル時に使用するよう定義します。
repositories {
mavenCentral()
}
dependencies {
compileOnly("jakarta.servlet:jakarta.servlet-api:6.0.0")
}
- ソースファイル、リソースファイル、Webコンテンツファイルなどを収容するディレクトリを生成します。
ソースディレクトリ | warファイル内への配置 | 備考 |
---|---|---|
src/main/java | WEB-INF/classes | コンパイルされたクラスファイルをwarに配置 |
src/main/resources | WEB-Inf/ | |
src/main/webapp | ルート直下 | 静的コンテンツなど |
helloWar % mkdir -p src/main/{java,resources,webapp} helloWar % ls src/main java resources webapp
静的コンテンツの作成とwarファイル生成¶
まずは、index.htmlを作成します。
- src/main/webapp/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Gradle web applicaiton samples</title>
</head>
<body>
<p>Hello, Gradle War World.</p>
</body>
</html>
ビルドしてwarファイルを作成します。
helloWar % ./gradlew war BUILD SUCCESSFUL in 717ms 1 actionable task: 1 executed helloWar % ls build/libs helloWar.war helloWar % jar tf build/libs/helloWar.war META-INF/ META-INF/MANIFEST.MF index.html
生成されたwarファイルは、プロジェクト名 helloWar に拡張子.warをつけた helloWar.warとなりました。
src/main/webapp/index.htmlがwarファイルのルートに配置されています。
warファイルをサーブレットコンテナに配置し実行¶
tomcatのwebappディレクトリ下に、helloWar.warファイルをコピーし、tomcatを起動します。
以下は、MacOSにhomebrewでインストールしたtomcat 10.1.19にコピーして実行する例です。
helloWar % cp build/libs/helloWar.war /opt/homebrew/opt/tomcat/libexec/webapp/ helloWar % catalina run : 31-Mar-2024 21:12:05.386 情報 [main] org.apache.catalina.startup.HostConfig.deployWAR Webアプリケーションアーカイブ [/opt/homebrew/Cellar/tomcat/10.1.19/libexec/webapps/helloWar.war] を配備します 31-Mar-2024 21:12:05.560 情報 [main] org.apache.catalina.startup.HostConfig.deployWAR Web アプリケーションアーカイブ [/opt/homebrew/Cellar/tomcat/10.1.19/libexec/webapps/helloWar.war] の配備は [171] ミリ秒で完了しました :
配置したhelloWar.warファイルがアプリケーションとして配備されたメッセージが表示されました。
tomcatでは、配置したwarファイルの名前(helloWar)がコンテキストパスとして使用されます。
そこで、ブラウザから http://localhost:8080/helloWar/ にアクセスすると、index.htmlが表示されます。
![]() |
サーブレットソースコードの作成¶
src/main/java の下に、パッケージ名に基づくディレクトリ階層でサーブレットのソースファイルを作成します。
- src/main/java/com/torutk/hello/HelloServlet.java
package com.torutk.hello;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.ZonedDateTime;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class HelloServlet extends HttpServlet {
@Override
public void doGet(
HttpServletRequest request, HttpServletResponse response
) throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.println("Hello, Gradle War World.");
out.println(ZonedDateTime.now());
}
}
サーブレットのソースコードは、コンパイルされてwarファイルのWEB-INF/classes以下にパッケージ階層に応じたディレクトリにクラスファイルが配置されます。
helloWar % jar tf build/libs/helloWar.war META-INF/ META-INF/MANIFEST.MF WEB-INF/ WEB-INF/classes/ WEB-INF/classes/com/ WEB-INF/classes/com/torutk/ WEB-INF/classes/com/torutk/hello/ WEB-INF/classes/com/torutk/hello/HelloServlet.class index.html
アプリケーション設定の記述¶
サーブレットコンテナに配置したサーブレットクラスへアクセスするURLのパスを定義する必要があります。
サーブレットクラスにアノテーションでパスを定義する方法と、web.xmlにパスを定義しサーブレットコンテナに配置する方法の2つがあります。
ここでは、web.xml に定義を記述します。
- src/main/webapp/WEB-INF/web.xml
お決まりの記述(schema, 名前空間の定義)
<?xml version="1.0" encoding="UTC-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
</web-app>
サーブレット毎の記述
- (サーブレット名、URLパターン)の定義
- (サーブレット名、サーブレットクラス名)の定義
ここでは、サーブレット名 HelloServlet、URLパターン名 /hello、サーブレットクラス名 com.torutk.hello.HelloServletとします。
<?xml version="1.0" encoding="UTC-8"?>
<web-app ...略>
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.torutk.hello.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
ローカルマシンでtomcat 10.1を動かして、tomcat の webappsディレクトリに helloWar.war を配置した場合、http://localhost:8080/helloWar/hello
をアクセスすると、サーブレットが実行されます。
web.xmlの定義では、次を記述します。
- <servlet-name> 定義対象のサーブレットをweb.xmlの設定上で識別する名前。実装したサーブレットのクラス名にする遠い
- <servlet-class> 定義対象のサーブレットのFQCN(完全修飾クラス名)を記述
- <url-pattern> 定義対象のサーブレットを呼び出すURLのコンテキストルート以下
http://サーバーDNS名:ポート番号/コンテキストパス/URLパターン
コンテキストパスは、tomcatの場合 webappsディレクトリ下に配置したアプリケーションのディレクトリ名(warファイルを配置した場合、warファイルの拡張子なしの名前のディレクトリが作られる)となります。
コンテキストパスを変更する場合は、warファイルのMETA-INF/context.xml に定義するようですが、tomcatでwarファイルの自動展開をすると、重複が発生してあまりうまくないようです。
バージョンの導入¶
一般的なアプリケーション・ライブラリの成果物(JARファイル等)は、ファイル名にバージョン識別子を含めることが多いです。例えば、myapp-0.15.3.jar のように。
しかし、Webアプリケーションの成果物(WARファイル)は、拡張子.warを除いたファイル名がコンテキストパスに使われることが多いようで、その場合バージョン識別子を含んだwarファイル(例、myapp-2.1.war)を提供してしまうと、WebアプリケーションにアクセスするURLにバージョンが含まれ、バージョン毎に異なってしまうという問題になります。例えば、https://the.server.example.com/myapp-2.1/hello となり、バージョンアップすると https://the.server.example.com/myapp-2.2.1/hello とURLが変わってしまいます。
対応はちょっと厄介で、アプリケーションサーバー共通の設定ファイルに、warファイルの配備の度にマッピングを書き換えて再起動するなどの処置が発生します。
- build.gradle.kts
version="1.0-SNAPSHOT"
- 生成されるwarファイル名にバージョン文字列が追加されます。
- helloWar-1.0-SNAPSHOT.war
warファイル名が変わると、URL(コンテキストパス)が変わってしまいます。
http://localhost:8080/helloWar-1.0-SNAPSHOT/hello
tomcat固有の解決法であれば、warファイルの名前を、コンテキストパス##バージョン.war(例:helloWar##1.0-SNAPSHOT.war)とすると、コンテキストパスは##の前の文字列となり、バージョンが##の後の文字列となります。
version="1.0-SNAPSHOT"
tasks.war {
archiveFileName = "helloWar##$version.war"
}
tomcatのwebappsディレクトリには、##が付いたファイルとディレクトリが展開されますが、コンテキストパスは##の前の名前となります。
webapps % ls -1 : helloWar##1.0-SNAPSHOT helloWar##1.0-SNAPSHOT.war :
メモ¶
warファイルに含めたいファイル¶
WEB-INF/lib下にjarファイル¶
- warプラグインは、dependenciesでruntimeに指定されたライブラリを、warファイルのWEB-INF/lib下に含める
- 注)providedCompileおよびprovidedRuntimeで指定したライブラリは、アプリケーションサーバーで用意されるライブラリを指定するもので、warファイルには含まれない