WebSphere LibertyでJava EEアプリケーションをOne-JAR化する

この記事は[Java EE Advent Calendar 2016](http://qiita.com/advent-calendar/2016/javaee)の21日目の記事です。昨日はemag@githubさんの[WildFly Swarm いろいろ 3 選 #javaee](http://qiita.com/emag@github/items/112c8030dce31eef3d7b)でした。

Java EEアプリケーションの配布は，一般的にEAR/WARファイルで行われますが，実行するにはアプリケーションサーバーの導入や構成が必要になってしまいます。`java -jar`で実行可能なJARファイルで配布できるといろいろ便利です。IBMのJava EEアプリケーションサーバー[WebSphere Liberty](https://developer.ibm.com/wasdev/)にも，実行可能なJARファイルをパッケージする機能が用意されています。

#WebSphere Libertyのダウンロード

WebSphere Libertyは，開発用途であれば無償でダウンロードして利用することができます。[WASDevダウンロードサイト](https://developer.ibm.com/wasdev/downloads/)から[ランタイムをダウンロード](https://developer.ibm.com/wasdev/downloads/liberty-profile-using-non-eclipse-environments/)しましょう。Java EE 7 Web Profileに対応したものFull Platformに対応したものやJava SDKを含んだものなどが用意されていますので，必要なものを選んでダウンロードします。導入はダウンロードしたZIPファイルを展開するだけです。

```
$ unzip wlp-webProfile7-16.0.0.4.zip
```

#構成の作成

wlp/binディレクトリの下のserverコマンドを使用してデフォルトの構成ファイルを作成することができます。ここではserver1という名前の作成しています。サーバー名を省略するとdefaultServerという名前のサーバーが作成されます。

```
$ cd wlp/bin
$ ./server create server1
```

wlp/usr/servers/server1ディレクトリにserver.xmlという構成ファイルが作成されています。環境によってデフォルトの構成ファイルは様々ですが，ここでは以下のように書き換えます。

```xml:server.xml
<?xml version="1.0" encoding="UTF-8"?>
<server description="new server">

    <!-- Enable features -->
    <featureManager>
        <feature>jsp-2.3</feature>
    </featureManager>

    <httpEndpoint id="defaultHttpEndpoint"
                  httpPort="9080"
                  httpsPort="9443" />

</server>
```

Libertyでは，提供されている機能がfeatureという独立したコンポーネントで実装されています。構成ファイルで指定されたfeatureだけがメモリにロードされて初期化されます。現在の環境で使用可能なfeatureは`productInfo featureInfo`コマンドで一覧が表示されます。足りないfeatureは`installUtility install adminCenter-1.0`のようにコマンドを実行するとダウンロード・導入ができます。

webProfile-7.0やjavaee-7.0という全部入りのfeatureも定義されていますが，なるべくなら使う機能のfeatureだけを指定すると，起動時間も短くなりますし，このあと作成するJARファイルも小さくなります。

ここではJSP 2.3（と，その前提となっているServlet 3.1）の機能だけを有効化しています[^1]。
[^1]: MicroProfileのパッケージをダウンロードして導入された場合はJSP 2.3のfeatureが含まれていません。その場合は`installUtility install jsp-2.3`でfeatureを追加導入してください。

#アプリケーションの作成

別途開発したWARファイルやEARファイルももちろん利用できるのですが，ここでは直接ディスク上にアプリケーションを作成していきましょう。

wlp/usr/servers/server1/dropinsにtest.warというディレクトリを作成します。これだけでLibertyにWebアプリケーションとして認識されます。そのなかにindex.jspというファイルを作成します。

```jsp:index.jsp
<html>
<head><title>Hello Liberty!</title></head>
<body>
<h1>Hello Liberty!</h1>
It is <%= new java.util.Date() %> now.
</body>
</html>
```

より実際的なserver.xmlの構成やアプリケーションの導入方法については，[こちら](http://www.ibm.com/developerworks/jp/websphere/library/was/was855liberty_ws/)の資料の[Libertyプロファイルによる開発・構成](http://public.dhe.ibm.com/software/dw/jp/websphere/was/was855liberty_ws/WASV855_UPDATE_03.pdf)などを参照してください。

#サーバーの実行

wlp/binディレクトリの下でserverコマンドを実行します。

```
$ ./server run server1
Java HotSpot(TM) 64-Bit Server VM バージョン 1.8.0 (ja_JP) で、server1 (WebSphere Application Server 16.0.0.4/wlp-1.0.14.cl160320160831-1555) を起動しています
[AUDIT   ] CWWKE0001I: サーバー server1 が起動されました。
[AUDIT   ] CWWKZ0058I: アプリケーションの dropins をモニター中です。 
[AUDIT   ] CWWKT0016I: Web アプリケーションが使用可能です (default_host): http://localhost:9080/test/
[AUDIT   ] CWWKZ0001I: アプリケーション test が 0.231 秒で開始しました。
[AUDIT   ] CWWKF0012I: サーバーは次のフィーチャーをインストールしました。[jsp-2.3, servlet-3.1, el-3.0]。
[AUDIT   ] CWWKF0011I: サーバー server1 は、Smarter Planet に対応する準備ができました。
```

ブラウザで http://localhost:9080/test/ にアクセスすると作成したJSPが実行されます。Ctrl+Cでサーバーを停止させることができます。

#サーバーのパッケージ

こうして作成したLibertyの実行環境＋サーバー構成＋アプリケーションを，まとめて一つのJARファイルにパッケージすることができます。

```
$ ./server package server1 --include=minify,runnable --archive=/tmp/server1.jar
サーバー server1 をパッケージ中です。
サーバー server1 で内容を照会しています。
Java HotSpot(TM) 64-Bit Server VM バージョン 1.8.0 (ja_JP) で、server1 (WebSphere Application Server 16.0.0.4/wlp-1.0.14.cl160320160831-1555) を起動しています
[AUDIT   ] CWWKE0001I: サーバー server1 が起動されました。
[AUDIT   ] CWWKF0026I: サーバー server1 は、より小さいパッケージを作成する準備ができました。
[AUDIT   ] CWWKE0036I: 1.439 秒後にサーバー server1 が停止しました。
サーバー server1 のアーカイブを作成しています。
サーバー server1 のパッケージが /tmp/server1.jar で完了しました。
```

できあがったJARファイルを見てみましょう。

```
$ ls -l /tmp/server1.jar 
-rw-r-----  1 takakiyo  wheel  27055142 12 21 17:16 /tmp/server1.jar
```

サイズは27Mバイトです。これを実行してみましょう。

```
$ java -jar /tmp/server1.jar 
ファイルを /Users/takakiyo/wlpExtract/server1_15934526082127/wlp に抽出しています。
すべての製品ファイルを正常に抽出しました。
Java HotSpot(TM) 64-Bit Server VM バージョン 1.8.0 (ja_JP) で、server1 (WebSphere Application Server 16.0.0.4/wlp-1.0.14.cl160320160831-1555) を起動しています
[AUDIT   ] CWWKE0001I: サーバー server1 が起動されました。
[AUDIT   ] CWWKZ0058I: アプリケーションの dropins をモニター中です。 
[AUDIT   ] CWWKT0016I: Web アプリケーションが使用可能です (default_host): http://localhost:9080/test/
[AUDIT   ] CWWKZ0001I: アプリケーション test が 0.163 秒で開始しました。
[AUDIT   ] CWWKF0012I: サーバーは次のフィーチャーをインストールしました。[jsp-2.3, servlet-3.1, el-3.0]。
[AUDIT   ] CWWKF0011I: サーバー server1 は、Smarter Planet に対応する準備ができました。
```

なんか一時ディレクトリに展開したりとズルしていますが，そのおかげでClassLoader#getResourceAsStream()なども問題なく動きます。一時ディレクトリの中身はサーバーを停止すると自動的に削除されます。

#環境変数の参照

作成したパッケージを修正することなく環境に応じた動作をさせることができると便利ですよね。ここでは構成ファイルの中で環境変数を参照する方法をご紹介します。

server.xmlのLISTENするポート番号を，以下のように`${env.環境変数名}`に修正します。

```xml
    <httpEndpoint id="defaultHttpEndpoint"
                  httpPort="${env.WLP_HTTP_PORT}"
                  httpsPort="9443" />
```

パッケージを作り直して，以下のように実行してみましょう。

```
$ export WLP_HTTP_PORT=8880; java -jar /tmp/server1.jar
```

ブラウザでアクセスしてみると，ポート番号が変わっていることがわかると思います。

#まとめ

WebSphere Libertyを使用するとJava EEアプリケーションもOne JARファイルとして配布することが可能になり，Webアプリケーションを簡単に配布することができるようになります。