はじめに
『こんな感じのモックアップをさくっと作ってよ。』このようなの突然の一言で、タスクをふられた経験は無いでしょうか。
障害調査やフィージビリティスタディなどを行う際に調査対象のアプリケーションと連携するモックアップアプリケーションを作ることがありますが、今回ふってきたタスクもこのモックアップアプリケーションの作成です。
モックアップアプリケーションは少ない手数で動くものができあがればよいので、今回はまだ私が使ったことのないさくっと作れそうなフレームワークを探すところから始めてみようと思います。
採用するフレームワークに設定した条件
- OSSで提供されているフレームワークであること
- Javaで実装できること
- 環境構築 ⇒ 実装/コンパイル ⇒ デプロイ/起動 までの作業がさくっとやれること(今回はここが最も大事)
- 可能な限りサーバーのリソースを消費しないこと
モックアップアプリケーションに実装する機能の要件
- 受信したHTTPリクエストの内容を後で確認できること
- JSON形式でデータをやり取りできること
- リクエストの特定のパラメーターによって応答結果(OK/NG)を切替えられること
実際に行った探し方
まず、OSSで提供されているフレームワークを**GitHubで、java web framework microservice
という検索キーワードで検索してみました。検索結果は以下のとおり。
★の数をみてtipsy/javalin**が第1候補、pippo-java/pippoが第2候補とします。
次に、公式サイトでフレームワークの特徴を確認し、今回の採用条件と実装する機能要件を満たせそうかサイトの記載を斜め読みます。
どうやら両方とも設定した条件を満たしているようなので、「カラフルでポップなアイコン
とサイズを意識した設計(組込みデバイスでの利用も想定しいる点)
」が気になったので、今回は第2候補のPippoを採用してみます。
- 気になったのは、Pippo公式サイトにあった以下の記述。
The core is small (around 140 KB) and we intend to keep it as small/simple as possible and to push new functionalities in pippo modules and third-party repositories/modules.
You are not forced to use a specific template engine or an embedded web server. Furthermore you have multiple out of the box options (see Templates and Server).
Also, Pippo comes with a very small footprint that makes it excellent for embedded devices (Raspberry Pi for example).
モックアップアプリケーション構築
早速、モックアップアプリケーションを開発環境を構築します。
環境構築
- JDK 1.8
- Maven 3
ソースファイルの取得
GitHubのレポジトリからローカル環境へレポジトリを取得します。
モックアップアプリケーションの雛形を作成
Quickstartの記載に従って、ローカル環境にCloneしたレポジトリのルートディレクトリpippo
へ移動して、以下のコマンドを実行します。
-DgroupId
と-DartifactId
の箇所のみ変更しています。
mvn archetype:generate \
-DarchetypeGroupId=ro.pippo \
-DarchetypeArtifactId=pippo-quickstart \
-DarchetypeVersion=1.5.0 \
-DgroupId=com.example.mock \
-DartifactId=example-mock
ビルドしたモジュールのバージョンを設定するか確認されるので変更する場合はここで入力します。
今回はデフォルトの1.0-SNAPSHOT
で良いので空のままEnterキーを押下します。
Define value for property 'version' 1.0-SNAPSHOT: :
続いて設定内容ついて確認されるので、問題がなければY
を入力します。
[INFO] Using property: package = com.example.mock
Confirm properties configuration:
groupId: com.example.mock
artifactId: example-mock
version: 1.0-SNAPSHOT
package: com.example.mock
Y: :
実装
ルートディレクトリpippo
以下に-DartifactId
で指定した名称example-mock
のディレクトリが作成されているので、以下のファイルを編集してモックアップアプリケーションを作成していきます。
pippo
└─ example-mock
├─ pom.xml (利用するモジュールの追加はこのファイルを編集します)
└─ src
└─ main
├─ java
│ └─ com
│ └─ example
│ └─ mock
│ ├─ PippoApplication.java (今回はこのファイルに処理を実装します)
│ └─ PippoLauncher.java
├─ resources
│ ├─ conf
│ │ ├─ application.properties
│ │ ├─ messages.properties
│ │ └─ messages_xx.properties etc (ロケールに応じたmessages.propertiesがいくつかあります)
│ ├─ templates
│ │ └─ hello.ftl
│ └─ simplelogger.properties (ロガーの設定はこのファイルを編集します)
└─ webapp
└─ WEB-INF
└─ web.xml
まずは、リクエストとレスポンスをマッピングするPOJOを作成します。
- リクエストPOJO
package com.example.mock.common;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class RequestParameter implements Serializable {
private static final long serialVersionUID = 1L;
public String itemA;
public Boolean itemB;
}
- レスポンスPOJO
package com.example.mock.common;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class ResponseParameter implements Serializable {
private static final long serialVersionUID = 1L;
// レスポンス結果(1:OK、0:NG)
public Integer result;
public String message;
}
次に、JSON形式のリクエストをPOSTで受付けた際に実行するロジックを実装します。
package com.example.mock;
import java.security.InvalidParameterException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.example.mock.common.RequestParameter;
import com.example.mock.common.ResponseParameter;
import ro.pippo.core.Application;
/**
* A simple Pippo application.
*
* @see com.example.mock.PippoLauncher#main(String[])
*/
public class PippoApplication extends Application {
private final static Logger log = LoggerFactory.getLogger(PippoApplication.class);
@Override
protected void onInit() {
getRouter().ignorePaths("/favicon.ico");
// send 'Hello World' as response
GET("/", routeContext -> routeContext.send("Hello World"));
// 今回実装した箇所はここ
POST("/reqJson", routeContext -> {
log.debug("Check Request Parameters:" + routeContext.getRequest().getParameters());
log.debug("Check Request Body:" + routeContext.getRequest().getBody());
// リクエストのBody部をリクエスト用のPOJOへマッピング
RequestParameter reqParm = routeContext.createEntityFromBody(RequestParameter.class);
if (null == reqParm) {
throw new InvalidParameterException("Request Paramater is Invalid");
}
ResponseParameter resParm = new ResponseParameter();
// リクエストのitemBの値を判定して応答を切替え
if (Boolean.FALSE == reqParm.itemB) {
// NG
resParm.result = Integer.valueOf(0);
resParm.message = "NG:" + reqParm.itemA;
log.debug("Check Result:NG");
} else {
// OK
resParm.result = Integer.valueOf(1);
resParm.message = "OK:" + reqParm.itemA;
log.debug("Check Result:OK");
}
// JSON形式のレスポンスを返却
routeContext.json().send(resParm);
});
}
}
simplelogger.properties
を編集し、今回埋め込んだログが出力されるように修正します。
また、ログ出力日時のフォーマットに年月日も出力されるように修正します。
org.slf4j.simpleLogger.log.com.example.mock=debug
org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss
コンパイル/起動
今回作成したモックアプリケーションは、Jacksonを利用するため、example-mock
ディレクトリ以下のpom.xml
へ以下の記述を追加します。
<!-- Jackson -->
<dependency>
<groupId>ro.pippo</groupId>
<artifactId>pippo-jackson</artifactId>
<version>${pippo.version}</version>
</dependency>
example-mock
ディレクトリ以下で、コマンドを実行します。
mvn compile exec:java
動作検証
-
ブラウザで
http://localhost:8338/
へアクセスし、Hello Worldが表示されることを確認します。 -
cURLコマンドでJSON形式のリクエストを送信し、応答結果を確認します。
- 応答結果OKのパターン
$ curl -H "Content-Type: application/json" -X POST "http://localhost:8338/reqJson" -d '{"itemA": "Test Request", "itemB": true}' -i
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 80 100 40 100 40 183 183 --:--:-- --:--:-- --:--:-- 197HTTP/1.1 200 OK
Date: Mon, 17 Dec 2018 02:53:48 GMT
Content-Type: application/json;charset=utf-8
Content-Length: 40
Server: Jetty(9.4.6.v20170531)
{"result":1,"message":"OK:Test Request"}
ログ出力結果
2018-12-17 11:53:48 [qtp188599000-16] DEBUG ro.pippo.core.PippoFilter - Request POST '/reqJson'
2018-12-17 11:53:48 [qtp188599000-16] DEBUG ro.pippo.core.route.DefaultRouter - Found 1 route matches for POST '/reqJson'
2018-12-17 11:53:48 [qtp188599000-16] DEBUG ro.pippo.core.route.DefaultRouteContext - Executing handler for POST '/reqJson'
2018-12-17 11:53:48 [qtp188599000-16] DEBUG com.example.mock.PippoApplication - Check Request Parameters:{}
2018-12-17 11:53:48 [qtp188599000-16] DEBUG com.example.mock.PippoApplication - Check Request Body:{"itemA": "Test Request", "itemB": true}
2018-12-17 11:53:48 [qtp188599000-16] DEBUG com.example.mock.PippoApplication - Check Result:OK
2018-12-17 11:53:48 [qtp188599000-16] DEBUG ro.pippo.core.route.RouteDispatcher - Returned status code 200 for POST '/reqJson'
- 応答結果NGのパターン
$ curl -H "Content-Type: application/json" -X POST "http://localhost:8338/reqJson" -d '{"itemA": "Test Request", "itemB": false}' -i
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 81 100 40 100 41 182 187 --:--:-- --:--:-- --:--:-- 187HTTP/1.1 200 OK
Date: Mon, 17 Dec 2018 02:53:55 GMT
Content-Type: application/json;charset=utf-8
Content-Length: 40
Server: Jetty(9.4.6.v20170531)
{"result":0,"message":"NG:Test Request"}
ログ出力結果
2018-12-17 11:53:55 [qtp188599000-19] DEBUG ro.pippo.core.PippoFilter - Request POST '/reqJson'
2018-12-17 11:53:55 [qtp188599000-19] DEBUG ro.pippo.core.route.DefaultRouter - Found 1 route matches for POST '/reqJson'
2018-12-17 11:53:55 [qtp188599000-19] DEBUG ro.pippo.core.route.DefaultRouteContext - Executing handler for POST '/reqJson'
2018-12-17 11:53:55 [qtp188599000-19] DEBUG com.example.mock.PippoApplication - Check Request Parameters:{}
2018-12-17 11:53:55 [qtp188599000-19] DEBUG com.example.mock.PippoApplication - Check Request Body:{"itemA": "Test Request", "itemB": false}
2018-12-17 11:53:55 [qtp188599000-19] DEBUG com.example.mock.PippoApplication - Check Result:NG
2018-12-17 11:53:55 [qtp188599000-19] DEBUG ro.pippo.core.route.RouteDispatcher - Returned status code 200 for POST '/reqJson'
ビルドの作成
動作検証が完了したら、Pippoサイトの記載を参考にexample-mock/src/main/assembly/assembly.xml
を作成します。assembly.xml
の内容は以下のとおり。
<?xml version="1.0" encoding="UTF-8"?>
<assembly>
<id>app</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<dependencySets>
<dependencySet>
<useProjectArtifact>false</useProjectArtifact>
<scope>runtime</scope>
<outputDirectory>lib</outputDirectory>
<includes>
<include>*:jar:*</include>
</includes>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<directory>${project.build.directory}</directory>
<outputDirectory>.</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
<excludes>
<exclude>*-javadoc.jar</exclude>
<exclude>*-sources.jar</exclude>
</excludes>
</fileSet>
</fileSets>
</assembly>
今回は、QuickStartの手順に沿っているためpom.xml
の編集は不要です。(モックアップアプリケーションの雛形を作成した際に既に記載されています)
アプリケーションサーバーJetty
を組込んだビルドexample-mock-1.0-SNAPSHOT.zip
がtarget
ディレクトリに作成されます。実行するコマンドは以下のとおり。
mvn clean package
なお、Tomcat等の別途アプリケーションサーバーが用意されている場合には、以下のコマンドを実行してモックアップアプリケーションのWarファイルをビルドします。
mvn -Pwar package
起動
作成したZipファイルexample-mock-1.0-SNAPSHOT.zip
を任意のディレクトリへ解凍し、解凍したディレクトリへ移動後に以下のコマンドを実行します。
java -jar example-mock-1.0-SNAPSHOT.jar
まとめ
駆け足で、Pippoを用いたモックアップアプリケーションを作ってみた感想ですが、さくっと作るという今回の条件を十分に満たしていることが確認できました。
設定した条件 | 評価 |
---|---|
OSSで提供されているフレームワークであること | ○ Apache 2.0ライセンスで提供されています |
Javaで実装できること | ○ |
環境構築からデプロイ/起動までの作業がさくっとやれること | ○ ドキュメントの記載もわかり易くてとくに詰ることなくさくっとできました |
可能な限りサーバーのリソースを消費しないこと | ○ ディスク上のサイズ、使用メモリーサイズともに小さいです |
個人的に楽しみにしていたWebアプリケーションのディスク上のサイズは、約5.9MBでした。(アプリケーションサーバー込み)
比較のためTomcat(8.5.30 : ディストリビューションはCore)にWarファイルをデプロイした際のディスク上のサイズを確認したところ、約14.5MBでした。ディスク上のサイズで2倍以上の差があり、かなり小さい印象をうけました。
また、JVMのヒープサイズもデフォルト値で動作させることができて、タスクマネージャーで確認したメモリー使用量は約61MBであり、組込みデバイスでの利用も考慮されているだけあって使用メモリーサイズへの配慮がみえます。
比較対象としてTomcatとJettyをデフォルト設定で起動した際のメモリー使用量は以下のとおりでした。
- Tomcat(8.5.30)が約110MB
- Jetty(9.4.14.v20181114)が約66MB
モジュール化されている他の機能についても機会があればご紹介しようと思います。