25
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

オープンソースまるごと by NRI OpenStandiaAdvent Calendar 2018

Day 24

突然の『さくっと作ってよ』に答える軽量フレームワークを探してみた

Last updated at Posted at 2018-12-23

はじめに

こんな感じのモックアップをさくっと作ってよ。』このようなの突然の一言で、タスクをふられた経験は無いでしょうか。

障害調査やフィージビリティスタディなどを行う際に調査対象のアプリケーションと連携するモックアップアプリケーションを作ることがありますが、今回ふってきたタスクもこのモックアップアプリケーションの作成です。

モックアップアプリケーションは少ない手数で動くものができあがればよいので、今回はまだ私が使ったことのないさくっと作れそうなフレームワークを探すところから始めてみようと思います。

採用するフレームワークに設定した条件

  • OSSで提供されているフレームワークであること
  • Javaで実装できること
  • 環境構築 ⇒ 実装/コンパイル ⇒ デプロイ/起動 までの作業がさくっとやれること(今回はここが最も大事)
  • 可能な限りサーバーのリソースを消費しないこと

モックアップアプリケーションに実装する機能の要件

  • 受信したHTTPリクエストの内容を後で確認できること
  • JSON形式でデータをやり取りできること
  • リクエストの特定のパラメーターによって応答結果(OK/NG)を切替えられること

実際に行った探し方

まず、OSSで提供されているフレームワークを**GitHubで、java web framework microserviceという検索キーワードで検索してみました。検索結果は以下のとおり。
★の数をみて
tipsy/javalin**が第1候補、pippo-java/pippoが第2候補とします。
github_search.png

次に、公式サイトでフレームワークの特徴を確認し、今回の採用条件と実装する機能要件を満たせそうかサイトの記載を斜め読みます。
どうやら両方とも設定した条件を満たしているようなので、「カラフルでポップなアイコンサイズを意識した設計(組込みデバイスでの利用も想定しいる点)」が気になったので、今回は第2候補のPippoを採用してみます。

pippo-logo.png

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
RequestParameter.java
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
ResponseParameter.java
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で受付けた際に実行するロジックを実装します。

PippoApplication.java

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へ以下の記述を追加します。

pom.xml
<!-- Jackson -->
<dependency>
  <groupId>ro.pippo</groupId>
  <artifactId>pippo-jackson</artifactId>
  <version>${pippo.version}</version>
</dependency>

example-mockディレクトリ以下で、コマンドを実行します。

mvn compile exec:java

動作検証

  1. ブラウザでhttp://localhost:8338/へアクセスし、Hello Worldが表示されることを確認します。

  2. 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の内容は以下のとおり。

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.ziptargetディレクトリに作成されます。実行するコマンドは以下のとおり。

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

モジュール化されている他の機能についても機会があればご紹介しようと思います。

参考情報

Pippo公式サイト

GitHubレポジトリ

25
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?