巨人の肩から銀の弾丸

Silver Bullet from the Shoulders of Giants - 銀の弾丸が難しくても、"銅"の弾丸くらいは撃てるように。Webソフトウェア開発に加え、自然言語処理、機械学習等の分野に触れる予定です。

JAX-RS/Glassfish/Swaggerでお手軽にはじめるAPIドキュメンテーション

こんにちは!
刺すような暑さが多少は和らいできて助かりますね。。。


さて、継続的インテグレーションの続編、を書く前に。

既存のAPIにほんのちょっと手を加えるだけで、
見た目もきれいで触って試せるドキュメントを簡単に作成できました。
そういう技術がいくつか出てきているようですが、自分で試したのは初めてだったので、
ハマったポイントなども併せて、こちらで紹介していきます。


f:id:tsumiki_brick:20150816115435p:plain

f:id:tsumiki_brick:20150816115520p:plain


Swagger Core Library

今回使用したライブラリです。

github.com

The goal of Swagger™ is to define a standard, language-agnostic interface to REST APIs which allows both humans and computers to discover and understand the capabilities of the service without access to source code, documentation, or through network traffic inspection.

ということで、

  • 言語の種類に依存せず
  • 人間にもコンピュータにも分かりやすい形で

REST APIへのインタフェースを提供してくれることを意図したライブラリです。
人間がAPIを理解するとき、ソースコードを読んだり、あるいはWikiとかにまとめたドキュメントを読んだりすると思いますが、

  • そもそもソースコードを読めない人がAPIを使う必要に迫られる
  • Wikiのドキュメントの更新が滞り、ドキュメントが正しいかどうか分からない

という問題は往々にして起こります。
そのような問題に対して、他にも以下のようなライブラリやサービスがソリューションを提供してくれていますが、

今回はひとまず、こちらの Swagger Core Libraryを試してみました。


Swagger UI

Swagger Core Libraryで作成したAPIの情報を、ブラウザに見やすく表示してくれるライブラリ。
特徴的なのは、"Try it out!"でcurlコマンドが見れたり、実際のレスポンスが見れたりするところ。
コードを見なくても、

  • 「ちゃんと4+3=7できてる!」
  • 「というかそれ以前にAPIがきちんと動いてる!」

のを確認できるのは便利ですね。

f:id:tsumiki_brick:20150816120004p:plain


環境

今回は、

  • Java EE 7
    • JAX-RS 2.x (jersey-common-2.1.jar)
  • NetBeans8.0.2
  • GlassFish4.1
  • swagger core 1.5.0

こんな環境で試してみました。


JAX-RSアプリケーション作成

NetBeans

New Project -> Maven -> Web Application

シンプルなJAX-RSアプリケーションを作成しておきます。


pom.xmlにSwaggerの依存を追加

JAX-RS 2.xのバージョンに合わせて、swagger-jersey2-jaxrsを選択します。
サンプルアプリ作成時の最新は
http://mvnrepository.com/artifact/io.swagger/swagger-jersey2-jaxrs/1.5.0
なので、pom.xmlに以下のように

<dependency>
  <groupId>io.swagger</groupId>
  <artifactId>swagger-jersey2-jaxrs</artifactId>
  <version>1.5.0</version>
</dependency>

依存を追加します。


Swaggerの設定と初期化、resourcesを追加

ApplicationConfig.javaの概略です。

import io.swagger.jaxrs.config.BeanConfig;

@ApplicationPath("webresources")
public class ApplicationConfig extends Application {


    // "simple.maven.glassfish.jaxrs.resource"
    private static final String RESOURCE_PACKAGE = SimpleResource.class.getPackage().getName();

    public ApplicationConfig() {
        BeanConfig beanConfig = new BeanConfig();
        beanConfig.setTitle("simpleMavenGlassfishJaxrs");
        beanConfig.setDescription("A simple Maven Glassfish JAX-RS project.");
        beanConfig.setVersion("1.0.2");
        beanConfig.setSchemes(new String[]{"http"});
        beanConfig.setHost("localhost:8080"); // ex. "localhost:8002"
        beanConfig.setBasePath("/simpleMavenGlassfishJaxrs/webresources");  // ex. "/api"
        beanConfig.setPrettyPrint(true);

        beanConfig.setResourcePackage(RESOURCE_PACKAGE); // ex. "io.swagger.resources"
        beanConfig.setScan(true);
    }


    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> resources = new java.util.HashSet<>();
        addRestResourceClasses(resources);

        // enable Swagger
        resources.add(io.swagger.jaxrs.listing.ApiListingResource.class);
        resources.add(io.swagger.jaxrs.listing.SwaggerSerializers.class);

        return resources;
    }

    private void addRestResourceClasses(Set<Class<?>> resources) {
        ...
    }

}


こちらのリンクに沿って、Applicationをextendsしているクラスで以下の設定を行います。

  • コンストラクタで基本情報の初期設定
    • setScanはいちばん最後に持ってくる
    • setVersionは自分のAPIのバージョンを指定するメソッドのよう
  • io.swagger.jaxrs.listing.* の2クラスを追加

web.xmlを使った方法など、別の方法もあるので、上記リンクから辿ってみてください。


アノテーションを追加

SimpleResource.javaクラスの概略です。

@Path("/simple")
@Api(tags = {"simple"})
public class SimpleResource {

    @GET
    @Path("/hello/{message}")
    @ApiOperation(value = "Hello Message API",
            notes = "Append 'Hello, ' before the message.",
            response = HelloMessage.class)
    public HelloMessage helloMessage(@PathParam("message") String message) {
        HelloMessage helloMessage = new HelloMessage();
        helloMessage.setMessage("Hello, " + message);
        return helloMessage;
    }

    ....

}
  • SimpleResourceクラス全体に@Apiを付ける
  • ドキュメントを作成したい各エンドポイントに、@ApiOperationを付ける

のが最低限の構成のようです。


確認

http://localhost:8080/simpleMavenGlassfishJaxrs/webresources/swagger.json
にアクセスして、自分のAPIの情報が見れていたら成功です。

{
  "swagger" : "2.0",
  "info" : {
    "description" : "A simple Maven Glassfish JAX-RS project.",
    "version" : "1.0.2",
    "title" : "simpleMavenGlassfishJaxrs"
  },
  "host" : "localhost:8080",
  "basePath" : "/simpleMavenGlassfishJaxrs/webresources",
  "tags" : [ {
    "name" : "simple"
  } ],
  "schemes" : [ "http" ],
  "paths" : {
    "/simple/hello/{message}" : {
      "get" : {
        "tags" : [ "simple" ],
        "summary" : "Hello Message API",
        "description" : "Append 'Hello, ' before the message.",
        "operationId" : "helloMessage",
        "parameters" : [ {
          "name" : "message",
          "in" : "path",
          "required" : true,
          "type" : "string"
        } ],
        "responses" : {
          "200" : {
            "description" : "successful operation",
            "schema" : {
              "$ref" : "#/definitions/hello message"
            }
          }
        }
      }
    },
    "/simple/path_param_addition/{op1}/{op2}" : {
      "get" : {
        "tags" : [ "simple" ],
        "summary" : "Path Param Addtion API",
        "description" : "Adds two numbers.",
        "operationId" : "pathParamAddition",
        "parameters" : [ {
          "name" : "op1",
          "in" : "path",
          "required" : true,
          "type" : "integer",
          "format" : "int32"
        }, {
          "name" : "op2",
          "in" : "path",
          "required" : true,
          "type" : "integer",
          "format" : "int32"
        } ],
        "responses" : {
          "200" : {
            "description" : "successful operation",
            "schema" : {
              "$ref" : "#/definitions/path param result"
            }
          }
        }
      }
    },
    "/simple/path_param_division/{op1}/{op2}" : {
      "get" : {
        "tags" : [ "simple" ],
        "summary" : "Path Param Division API",
        "description" : "Divide the first number by the second number.",
        "operationId" : "pathParamDivision",
        "parameters" : [ {
          "name" : "op1",
          "in" : "path",
          "required" : true,
          "type" : "integer",
          "format" : "int32"
        }, {
          "name" : "op2",
          "in" : "path",
          "required" : true,
          "type" : "integer",
          "format" : "int32"
        } ],
        "responses" : {
          "200" : {
            "description" : "successful operation",
            "schema" : {
              "$ref" : "#/definitions/path param result"
            }
          }
        }
      }
    }
  },
  "definitions" : {
    "hello message" : {
      "type" : "object",
      "required" : [ "message" ],
      "properties" : {
        "message" : {
          "type" : "string",
          "description" : "message"
        }
      }
    },
    "path param result" : {
      "type" : "object",
      "required" : [ "result" ],
      "properties" : {
        "result" : {
          "type" : "string",
          "description" : "result"
        }
      }
    }
  }
}

よさそうな雰囲気ですね。


Swagger UI

続いて、さっきのswagger.jsonをもとにドキュメンテーションのUIを作ります。
特別な設定は必要なく、

などが分かりやすいかと思います。
dist内のindex.htmlをブラウザで開いても大丈夫そう。


スクリーンショット

http://localhost:8080/swagger-ui-dist
にアクセスして、
最上部にあるAPIのURL欄に自分のswagger.jsonまでのURLを入れると、記事冒頭のスクリーンショットが見られます!


はまった点

と、ここまでスムーズにいけば良いのですが、
自分の場合は、swagger.jsonがスカスカになるという問題がありました。

{"swagger":"2.0","info":{"version":"1.0.2"},"host":"localhost:8080/simpleMavenGlassfishJaxrs/webresources","basePath":"/simple","schemes":["http"]}


これは

Swaggerの設定と初期化、resourcesを追加


のところで、サンプルをコピペしていたのが原因だったので、

beanConfig.setResourcePackage("io.swagger.resources");

上の手順にあるように、きちんと自分のリソースクラスを指定したのですが、

private static final String RESOURCE_PACKAGE = SimpleResource.class.getPackage().getName();
....
beanConfig.setResourcePackage(RESOURCE_PACKAGE);


今度はアプリケーション自体がGlassfishにデプロイできなくなるはめに。

java.lang.NoSuchMethodError: com.fasterxml.jackson.databind.AnnotationIntrospector.findPropertyIndex(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Ljava/lang/Integer;
    at io.swagger.jackson.ModelResolver.resolve(ModelResolver.java:372)
    at io.swagger.jackson.ModelResolver.resolve(ModelResolver.java:151)
    at io.swagger.converter.ModelConverterContextImpl.resolve(ModelConverterContextImpl.java:85)
    at io.swagger.jackson.ModelResolver.resolveProperty(ModelResolver.java:131)
    at io.swagger.jackson.ModelResolver.resolveProperty(ModelResolver.java:93)
    at io.swagger.converter.ModelConverterContextImpl.resolveProperty(ModelConverterContextImpl.java:65)
    at io.swagger.converter.ModelConverters.readAsProperty(ModelConverters.java:58)
    at io.swagger.jaxrs.Reader.parseMethod(Reader.java:768)
    at io.swagger.jaxrs.Reader.read(Reader.java:286)
    at io.swagger.jaxrs.Reader.read(Reader.java:169)
    at io.swagger.jaxrs.Reader.read(Reader.java:146)
    at io.swagger.jaxrs.config.BeanConfig.setScan(BeanConfig.java:170)
    ....


NetBeansでアプリケーションのDependencyを確認してもjackson-databind-2.4.2.jarが入っているし、このバージョンではfindPropertyIndexメソッドもきちんと実装されているのに・・・?

結果的には、実はアプリケーションの問題ではなかったので、
Glassfishで使われているjacksonのバージョンが古いのでそれを新しくすることで問題が解決しました。


おわりに

新規のアプリからでも、既存のアプリからでも、
手間をかけずにお手軽にAPIドキュメントを作成して、楽しく開発しましょう!


Git, Maven, Jenkins, GlassFishを使った継続的インテグレーション(CI) (1)

こんにちは。
今日は、プライベートで最近試している開発環境整備の一環として、
Jenkinsを使った自動テスト・ビルドについて書きたいと思います。

What is CI?

Continuous Integration(継続的インテグレーション
1度限りもしくは数回限りではなく、継続的に小まめにビルドを実行していくプラクティス
(http://www.atmarkit.co.jp/ait/articles/1309/04/news022.html)


Merits

ここも、上記(http://www.atmarkit.co.jp/ait/articles/1309/04/news022.html)の引用ではありますが・・・。

  • 想定外の状況が起こったときに、素早く検知できる
    • 仕込んでしまったバグを即座に検知できる
  • 開発者の環境依存の問題を検知しやすくなる
    • 「私の環境では動くけど・・・」
  • ビルドが属人化せずに画一的になりやすい
    • 「◯◯さんがビルドしたら失敗/成功した」

こういう状況に思い当たる節のある人も、少なくないのではないでしょうか・・・。


とはいえ

  • 導入が面倒そう
    • 一部のメンバーだけで利用してもOK
    • 手動ビルド・テストと組み合わせてもOK
  • まずは簡単なところから
    • ex. ビルドエラーが起きた場合はコミット者にメールを通知する

そうですね。できるところからはじめたいものです。


Sample development flow試してみました

最終的には、仕事で関わっているプロジェクトに還元したいのですが、
まずは全体像をつかむために、最小限のサンプルアプリを動かすことにします。

Environments

開発フローは、masterから機能ごとにブランチを切るGitHubフローにしてみました。
git-flowは少し複雑すぎる印象を受けたので・・・。

使わなかったEnvironments

次の機会に試してみたいです。

構成図

こちらのサイト(http://qiita.com/hiroshik1985/items/6433d5de97ac55fedfde)より引用
サンプルアプリでは、RDSにもアクセスしていないし、replicationも実はしていませんが(笑)
ELB経由でEC2インスタンスにアクセスします(図の左半分)。
簡単のため、このEC2インスタンス1つに、JenkinsもGlassfishも載せています。

f:id:tsumiki_brick:20150726003617p:plain


demoの流れ

  • 説明的な名前のブランチをmasterから作成する (add-two-number-division)
  • 作成したブランチでローカル開発/プッシュ
  • Pull Request を作成
    • 自動テストNG (11/2 = 5?)
    • テストの成功数、coverage、バグ温床、スタイルの問題、を指摘してくれる
  • 再度、ローカル開発
  • 再度、プッシュ
    • 自動テストNG
  • 再度、ローカル開発/プッシュ
    • 自動テストOK
  • masterへマージ
  • なるべく早くデプロイ

準備

サンプルプロジェクトを準備

JAX-RSを使ったREST APIと、それを表示するためのフロントエンドをAngularJSでコーディングしました。
仕様は・・・見ての通り、超カンタンにしてます。
自動テストの確認もしたいので、JUnitREST API単体テストも作っておきます。


f:id:tsumiki_brick:20150726003900p:plain


AWSの設定

基本的に上記サイト (http://qiita.com/hiroshik1985/items/6433d5de97ac55fedfde) に従って設定しました。

Jenkinsのインストール

ローカルからEC2インスタンスにログインし

ssh -i ~/.ssh/aws/[キーペアファイル… ex. xxx.pem] ec2-user@[Public IP/Elastic IP… ex. 5X.XX.XXX.XX]

Jenkins公式 (http://pkg.jenkins-ci.org/redhat/) に従ってインストールします。

sudo wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo
sudo rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key
(sudo) yum install jenkins

Glassfishなど他のアプリとかぶらないポート(例えば9696)にするには、

ls -ltr /etc/sysconfig/jenkins
vi /etc/sysconfig/jenkins
==============================
JENKINS_PORT=“8080”
↓
JENKINS_PORT=“9696==============================

ポート変更できたら、

# Jenkinsのサービスを起動
sudo service jenkins start # (restart, stopもある)
# 自動起動の設定
sudo chkconfig jenkins on
# ステータス確認
sudo service jenkins status
# 最新化
sudo yum update


ELB経由でEC2上のJenkinsにアクセスできるように、AWSコンソールから設定します。

# 本来はSSLでやった方がいい気がしますが・・・
Security Groups 
    test-web-elb ※任意のIP→ELBに、ポート9696でアクセスさせる
        Custom TCP Rule    TCP    9696    0.0.0.0/0
    test-web ※ELB→webは、VPC内アクセスを許可する
        Custom TCP Rule    TCP    9696    10.0.0.0/16

Load Balancers - Listeners ※ ELBからEC2インスタンスへ繋ぐ
    HTTP    9696    →    HTTP    9696


最後に、ブラウザからJenkinsのホーム画面にアクセスできたらOK!
http://[ELBのURL]:9696/

こちらのリンク (http://dev.classmethod.jp/cloud/aws/jenkins_on_ec2/) などを参考に、Jenkinsのセキュリティを設定しましょう。


Glassfishのインストール+warデプロイ

公式サイト (https://glassfish.java.net/download.html#gfoseTab) から、glassfish-4.1-web.zip をローカルにダウンロードします。
インストールは自分はこちらの手順 (http://qiita.com/digdagdag/items/4d26b477bfa3c51771f8) などを参考にしました。

ちなみに、「ローカル以外からGlassFish共通操作画面に接続するには、管理者(admin)パスワードを設定しSSLを有効にする必要がある。」(http://upstart.jp/blog/?p=309)
そうです。

SSLエラーが出たので、Elastic Load Balancing の SSL 証明書を作成 (http://docs.aws.amazon.com/ja_jp/ElasticLoadBalancing/latest/DeveloperGuide/ssl-server-cert.html)。

SSL connection error
Unable to make a secure connection to the server. This may be a problem with the server, or it may be requiring a client authentication certificate that you don't have.


再起動して、デフォルトドメインのdomain1が起動中なのを確認します。

sudo bin/asadmin restart-domain
sudo bin/asadmin list-domains

f:id:tsumiki_brick:20150726004559p:plain


最後に、サンプルアプリのwarをデプロイして、開発の準備は完了です。


開発フロー

とここまで書いたところで、肩が凝ってきてしまったので、
今日は一旦筆を置こうと思います。
続編では、「demoの流れ」の部分で書いた開発フローを
自作自演で(笑)試してみようと思います。

それでは、よい週末を!

応用情報技術者試験に合格しました!

はじめまして。tsumiki_brickです。

 

4月に受験した応用情報技術者試験の合否が、つい昨日公開されました。

https://www.jitec.ipa.go.jp/1_11seido/ap.html

 

平成27年度春期の試験に無事合格することができたので、

今後受験する方の参考になるような記事を書ければと思います。

 

目次

  • 受験の動機
  • 使用した素材
    • 参考書
    • Webサイト
    • 過去問
  • 勉強の流れ
  • Tips
  • おわりに

 

受験の動機

  • 2011年の特別試験

大学で情報系学科に進み、一通り知識を得ていた(つもりだった)こともあって、

腕試しのつもりで(基本情報を飛ばして)受験してみました。

・・・午前は8割近く得点するも、午後は50点台後半で惜敗。

 

  • 2015年の春試験

時は流れ、いつの間にか都内の某IT系企業に入社。

2015年に入った冬休み頃、ふとAPのことを思い出し、

あの試験を攻略しておきたいという気持ちで再受験しました。

APの謳い文句である

「高度IT人材となるために必要な応用的知識・技能をもち、高度IT人材としての方向性を確立した者」

って自分の手が届く範囲になったのかな、というのも興味がありました。

・・・合格できました!

 

 

f:id:tsumiki_brick:20150620015152p:plain

 

 

 

使用した素材

参考書

過去問CD付 応用情報技術者スーパー合格本〈2013年版〉

過去問CD付 応用情報技術者スーパー合格本〈2013年版〉

 

午前問題の部分だけ使いました。

分量が多めですが、試験の全体像を把握するのに適している本だと感じました。

分からない点を調べる「辞書」としても使えそうです。

(+) 要所要所で解説が丁寧。

(-) 誤植が多い。答えを隠さないと見えてしまう。

 

こちらも、午前問題の部分だけを使用。

教科書的な本を使って基本を一通り理解した後の午前対策に役立ちました。

(+) コンパクトなので混雑した電車の中でも使いやすい。

(-) 疑問点の解消にはwebや他の文献を参照する機会が多くなる。

 

2015 応用情報技術者午後問題の重点対策 (午後問題対策シリーズ)

2015 応用情報技術者午後問題の重点対策 (午後問題対策シリーズ)

 

名前の通り、午後試験の対策本です。

他の方々のブログで評判が良かったので購入してみましたが、とても役に立ちました。

(+) 解説が非常に丁寧。テーマが毎回異なる午後問題に対応できる「考え方」重視の構成。

(-) 強いて言えば、既に知識のある人には冗長かもしれないコラム

 

Webサイト

http://www.ap-siken.com

解説も丁寧で、スマホ対応もしていて使いやすかったです。

強いて言えば、問題ページから一覧ページに戻るとき、まえ見ていた場所に戻ってくれると便利かも。

 

過去問

H23-H26あたりを過去数年分解きました。

 

 

勉強の流れ

スーパー合格本を解く

ポケットスタディで午前の勉強

『午後問題の重点対策』を信じて解き始める

過去問

試験当日

 

短い時間で効率よい合格を目指すなら、

他の方々もオススメしているように、まず過去問を時間通りに解いてみて、

自分の勉強すべき分野を把握するのがいいかもしれません。

 

自分は、2011年に午後落ちしてからの2度目の受験でした。

4年も間が空くと、理解していたはずの事項がほとんど頭から抜けていたので、

ウォームアップの意味でも参考書を一通りさらうことから始めました。

 

『午後問題の重点対策』は、

「平日の帰宅後に1問以上解く」というのが、自分の中でいいペースメーカーになっていたように思います。

 

Tips

  • スーパー合格本(参考書)を一通りやらない

自分が体験した上で思ったのですが、まずは参考書を一通りやってから問題演習に、という方法は、

結果的にはあまり良くなかったのかもしれません。

そもそも、試験に事実上出題されない(うえに実務でも大して役に立たない)事項も多く載っています。

「問題解く→解説を理解する→分からないところをwebや参考書で調べる」

この流れの方が、効率的だと感じました。

 

  • まとまった時間を何度か作る

→特に午後試験に関しては、制限時間内に問題を解き切ることが大事になります。

本番と同じ時間で(できれば、本番通り眠気に襲われやすい午後一番に)問題演習したり、

足りていない知識を集中して詰め込んだりする時間を作れると、

合格までの道のりがグッと楽になると思います。

 

  • 午後問題で解答する分野を絞り込む

APほど出題範囲が広い試験だと、どうしても自分の苦手な分野からの出題も増えてきます。

午後試験では、11問中、1問必須**、2問から1問選択必須*、8問から4問選択でした。

本番の試験で問題選択に多くの時間を割いてしまうのはもったいないので、

「本命の分野」、「本命が難しかったら解く分野」、「おそらく解かない分野」の見当を事前につけるといいかもしれません。

自分の場合は、以下のようにある程度の目処を立てていました。

 

○「本命の分野」

情報セキュリティ**
プログラミング*
ネットワーク
データベース

情報システム開発

 

△「本命が難しかったら解く分野」

経営戦略*
プロジェクトマネジメント

サービスマネジメント

 

×「おそらく解かない分野」

組込みシステム開発(なじみがなく、公式を覚えるのも面白くなかった)

システム監査(規格や標準を覚えるのが楽しくなかった)

 

  • 午前試験で途中退出する

APは午前と午後、合わせて5時間の長丁場です。

合格点を取れると確信できたら、さっさと会場を出て、

休憩や午後試験の対策に時間を使う方がいいと思います。

会場近くのレストランも早ければ早いほど混みにくいというメリットもあります。

 

 

おわりに

以上、個人的な体験談でした。

「高度IT人材となるために必要な応用的知識・技能をもち、高度IT人材としての方向性を確立した者」になれたかと言えば、正直怪しいですが、

合格を機に、具体的な技術や専門的な知識を身につけていこうというモチベーションは上がりました。

もし気になる部分があれば、参考にしてみて下さい!