導入
この記事の目的
備忘録です。
やったこと
DockerのJettyイメージを使って、JavaサーブレットのHello Worldを実装しました。
サーブレットやWARに必要なものをいつも忘れてしまうので、メモとして残します。
使ったツール
- Docker
- Docker Compose
- Jetty
- Gradle
Docker
コンテナ型仮想化基盤。
Jettyのインストールの手間を省くため、そのままで動く仮想環境を構築するために使いました。
Docker Compose
複数のDockerコンテナの管理を補助するツール。
なくてもDockerは使えますが、dockerコマンドに渡す引数が複雑になりがちなので、それらをYAMLファイルとして残せるツールとして使いました1。
Jetty
Javaサーブレットコンテナ。
WARをデプロイして使えるアプリケーションサーバーです。
Gradle
Javaビルドツール。
依存関係の定義と、WARをビルドするために使いました。
検証環境
- macOS Sierra (10.12.3)
- Docker for Mac (1.13.0)
- Docker Compose (1.10.0)
- JDK (1.8.0_102)
- Gradle (3.2.1)
実施手順
Jettyコンテナを起動する
作業フォルダー直下に以下のファイルを作成します:
# バージョン2記法で記述する
version: '2'
# 起動するコンテナを定義する
services:
# Jettyコンテナ
jetty:
# Dockerfileからビルドするのではなく、Docker Hubの公式イメージを使う
# サイズが小さいAlpineベースのイメージを使う
image: jetty:9.4.1-alpine
# コンテナ内のポートをホストに公開する
# 書式 "HOST:CONTAINER"
ports:
# Jettyのポートを同じ番号でホストに公開する
- "8080:8080"
docker-compose.ymlと同じフォルダーでdocker-compose up
コマンドを実行します。
すると以下のようにJettyコンテナが起動し、Jettyのログが表示されます:
Mac-mini-K:jetty-docker kazuma$ docker-compose up
Creating jettydocker_jetty_1
Attaching to jettydocker_jetty_1
jetty_1 | 2017-02-09 10:14:37.178:INFO::main: Logging initialized @460ms to org.eclipse.jetty.util.log.StdErrLog
jetty_1 | 2017-02-09 10:14:37.417:INFO:oejs.SetUIDListener:main: Setting umask=02
jetty_1 | 2017-02-09 10:14:37.426:INFO:oejs.SetUIDListener:main: Opened ServerConnector@6767c1fc{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
jetty_1 | 2017-02-09 10:14:37.427:INFO:oejs.SetUIDListener:main: Setting GID=101
jetty_1 | 2017-02-09 10:14:37.429:INFO:oejs.SetUIDListener:main: Setting UID=100
jetty_1 | 2017-02-09 10:14:37.434:INFO:oejs.Server:main: jetty-9.4.1.v20170120
jetty_1 | 2017-02-09 10:14:37.456:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///var/lib/jetty/webapps/] at interval 1
jetty_1 | 2017-02-09 10:14:37.477:INFO:oejs.AbstractConnector:main: Started ServerConnector@6767c1fc{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
jetty_1 | 2017-02-09 10:14:37.478:INFO:oejs.Server:main: Started @760ms
http://localhost:8080/ にアクセスすると、Jettyからの404が返ってくるので、起動確認ができます:
WARを作成する
Eclipseのプロジェクトを作成する
作業フォルダーに、src/main/javaフォルダーを作成しておきます。
また、以下のファイルも作成し、同じフォルダーでgradle eclipse
を実行します:
// Javaのビルドスクリプト
// プラグイン指定
// Javaをコンパイルするのに必要
apply plugin: 'java'
// フォルダーをEclipseプロジェクト化するのに必要
apply plugin: 'eclipse'
// WARを生成するのに必要
apply plugin: 'war'
// リポジトリーとしてMavenリポジトリーを指定
repositories {
mavenCentral()
}
// 依存関係を記述する
dependencies {
// providedCompile: コンパイル依存だがWARに含めない
// サーブレットを作成するのに最低限必要なJARを追加
providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
}
作業フォルダーがEclipseプロジェクトになるので、以下のようにEclipseへインポートしておきます:
サーブレットとweb.xmlを作成する
以下のようなサーブレットクラスを作成します。
ネームスペースは任意のものをつけてください:
package com.github.kazuma1989.jetty;
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 TestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=UTF-8");
// GETメソッドのときは、パラメーターによらず Hello World! を返すのみ
PrintWriter out = resp.getWriter();
out.println("Hello World!");
}
}
また、src/main/webapp/WEB-INFフォルダーを作成し、以下のファイルを作成します:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID"
version="3.1">
<display-name>TestDynamicWebApp31</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>TestServlet</servlet-name>
<servlet-class>com.github.kazuma1989.jetty.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/TestServlet/*</url-pattern>
</servlet-mapping>
</web-app>
<servlet-class>com.github.kazuma1989.jetty.TestServlet</servlet-class>
の箇所は、先ほど作成したサーブレットクラスのFQCNを記入します。
※ 参考リンク [備忘録!]GradleでWARを作る - Qiita
作業フォルダーでgradle war
コマンドを実行すると、build/libs以下にWARが生成されます:
Mac-mini-K:jetty-docker kazuma$ gradle war
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:war UP-TO-DATE
BUILD SUCCESSFUL
Total time: 0.868 secs
Mac-mini-K:jetty-docker kazuma$ ls build/libs/
jetty-docker.war
JettyにWARをデプロイする
docker-compose.ymlの最後に以下の記述を追加します。
ホストのbuild/libsフォルダーにあるWARを、Jettyコンテナ内の/var/lib/jetty/webappsに置くことでデプロイします:
…
# コンテナ内のフォルダーをホストとマウントする
# 書式 HOST(docker-compose.ymlからの相対パス):CONTAINER(絶対パス)
volumes:
# Jettyのデプロイフォルダーに、GradleがWARを生成する先をマウントする
# foo.war には http://localhost:8080/foo でアクセスできるようになる
- ./build/libs:/var/lib/jetty/webapps
docker-compose.ymlを修正したので、Jettyコンテナを作り直します。
念のため削除して、再生成します:
Mac-mini-K:jetty-docker kazuma$ docker-compose stop && docker-compose rm -f
Stopping jettydocker_jetty_1 ... done
Going to remove jettydocker_jetty_1
Removing jettydocker_jetty_1 ... done
Mac-mini-K:jetty-docker kazuma$ docker-compose up
Creating jettydocker_jetty_1
Attaching to jettydocker_jetty_1
jetty_1 | 2017-02-09 10:45:01.994:INFO::main: Logging initialized @524ms to org.eclipse.jetty.util.log.StdErrLog
jetty_1 | 2017-02-09 10:45:02.278:INFO:oejs.SetUIDListener:main: Setting umask=02
jetty_1 | 2017-02-09 10:45:02.289:INFO:oejs.SetUIDListener:main: Opened ServerConnector@6767c1fc{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
jetty_1 | 2017-02-09 10:45:02.289:INFO:oejs.SetUIDListener:main: Setting GID=101
jetty_1 | 2017-02-09 10:45:02.291:INFO:oejs.SetUIDListener:main: Setting UID=100
jetty_1 | 2017-02-09 10:45:02.297:INFO:oejs.Server:main: jetty-9.4.1.v20170120
jetty_1 | 2017-02-09 10:45:02.320:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///var/lib/jetty/webapps/] at interval 1
jetty_1 | 2017-02-09 10:45:02.623:INFO:oeja.AnnotationConfiguration:main: Scanning elapsed time=71ms
jetty_1 | 2017-02-09 10:45:02.832:INFO:oejs.session:main: DefaultSessionIdManager workerName=node0
jetty_1 | 2017-02-09 10:45:02.832:INFO:oejs.session:main: No SessionScavenger set, using defaults
jetty_1 | 2017-02-09 10:45:02.833:INFO:oejs.session:main: Scavenging every 600000ms
jetty_1 | 2017-02-09 10:45:03.113:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@20398b7c{/jetty-docker,file:///tmp/jetty/jetty-0.0.0.0-8080-jetty-docker.war-_jetty-docker-any-8862506664121339371.dir/webapp/,AVAILABLE}{/jetty-docker.war}
jetty_1 | 2017-02-09 10:45:03.120:INFO:oejs.AbstractConnector:main: Started ServerConnector@6767c1fc{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
jetty_1 | 2017-02-09 10:45:03.121:INFO:oejs.Server:main: Started @1650ms
jetty-docker.warを読み込んでいるログが出力されています。
再び http://localhost:8080/ にアクセスすると、先ほどはなかったリンクが表示され、WARが読み込まれたことを示しています:
http://localhost:8080/jetty-docker/TestServlet/foo (fooの部分は任意)にアクセスすると、作成したサーブレットが実行され、成功です:
まとめ
自作のサーブレットクラスをJettyにデプロイし、実行する手順メモを残しました。
作成したファイルは以下の4つだけなので、非常に単純です。
build.gradle
docker-compose.yml
src/main/java/com/github/kazuma1989/jetty/TestServlet.java
src/main/webapp/WEB-INF/web.xml
Javaサーブレットで手取り早く何かを試したいときに有用かもしれません。
-
Jettyコンテナを8080ポートで起動するだけでも、
docker run -p "8080:8080" -t jetty:9.4.1-alpine
のように、シェルファイルに書き残したくなる引数の多さです。ボリュームをマウントするとなおさらです。 ↩