Kengo's blog

Technical articles about original projects, JVM, Static Analysis and TypeScript.

javadoc.ioのクローンをreactorで実装した

また技術キャッチアップ用のプロジェクト作りました。今回使いたかったのは以下の4つ:

  • spring-boot v2.0(リリース前)
  • spring-webflux v5.0(リリース前)
  • selenide v4.5
  • checker framework v2.2

Springは5.0リリース前にマイルストーン版を使って感覚を見たかったため、特にreactorとRxJavaの違いを確認したかったため。 selenideはユースケースを見たことが無かったのでなんの役に立つのか肌感覚を持つため。 checker frameworkは公式ドキュメントに色々不明点がある*1ので、(今までやってきたライブラリやCLIツールではなく)自走するウェブアプリで色々確かめたかったためです。

以下所感をまとめておきます。

reactorによるウェブアプリ実装について

vert.xでも感じたことですが、reactive-streamsを使ってのウェブアプリ実装は十分に可能なものの、どの程度ペイするのかよく分からないというのが実情です。 外部サーバとのI/Oが処理のほとんどとなりやすいウェブアプリにおいて非同期I/Oを短く簡潔に扱えるというのは利点ですが、リアクティブに処理を書く必要性・Backpressureの利点をきちんと享受したいケースがどの程度あるかというと疑問という感じでしょうか。 これについてはそれなりの負荷がかかるサービスで比較検証する必要がありそうです。ab等を使った負荷検証をしてみます。

また静的解析ツールの支援を受けられないのはなかなかつらいなと思いました。 subscribe()時に正常系のconsumerだけ設定したためにエラーに気づけなかったケースとか、Mono.create()Mono.from()の違いを知らずonNext signalを掴み損ねたケースとか、習熟すればすぐに見つけられるがそうじゃないとかなり厳しい「書き方の問題」が多数ありました。年月が解決する問題ではありますが、現時点では業務用途はだいぶきつい印象です。

selenideでできることはコード短縮だけでないという話

Selenideを使ってみて「あれこれって大してコード量減らないのでは……?」と思ったのでつぶやいた結果、公式アカウントに突っ込まれました。

ブラウザのレンダリングを待つようなコードをいちいち埋めないでいいというのは、確かに大きな利点です。特にSeleniumを使ったテストの経験が浅い開発者は、気軽に Thread.sleep(long) してしまいテストを遅くする傾向にあります。コードレビューで指摘し育てるのも手ですが、Selenideのようなソリューションで統一的に解決してしまうのは手でしょう。

ただリダイレクト周りの検証とか、いまいち書き方がわからずWebDriverを直接叩くケースはまだまだありそうな感じでした。これだけ知っていればSeleniumについて気にしないで良い、というモノでは無さそうです。

checker framework

ちょっとまだfalse-positiveや価値の低い解析結果が多い印象です。Java標準APIはデータベースが整ってきているようですが、ライブラリ側(今回で言えばreactor含むSpring Framework)のデータベースが無いため、Nullableを許容するAPIでも「ここはNonnullだから!null渡さないで!」とコンパイルエラーになることが多いです。結局SuppressWarningをつけて回避しています。

彼らが配布するQualifierの認知度が上がり利用されるようにならないと、まだ積極的な導入は難しそうという印象を持ちました。

*1:サンプルプロジェクトがcompileOnlyではなくcompileでchecker frameworkに依存してるのはなぜ?等

SpotBugsプラグイン実装方法2017

過去にFindBugsプラグインの実装方法について記事にしたとおり、FindBugsプラグインの実装には複雑なハックが必要でした。特にfindbugs.xmlやmessages.xmlなどのメタデータ管理が煩雑でした。

これがSpotBugs 3.1.0-RC3ではある程度楽になっているので、シンプルになった方法をここにまとめておきます。

プロジェクトの雛形を作る

archetypeがコミュニティから提供されるようになりました。これを使えばプラグイン開発の知識がなくてもすぐにプロジェクトを作成できます。

github.com

使い方は他のmaven archetypeと同じで、archetype:generate実行時にarchetypeのgroupId, artifactIdそしてversionを指定するだけです。現状バージョン0.1.0が最新なので、以下のコマンドを実行してください:

$ mvn archetype:generate \
    -DarchetypeArtifactId=spotbugs-archetype \
    -DarchetypeGroupId=com.github.spotbugs \
    -DarchetypeVersion=0.1.0

バグとして検出すべきケースを実装し、単体テストを回す

プロジェクト生成に成功すると、src/test/javaに2つのクラスが見つかるはずです。

これらを実際に解析して結果を確認しているテストケースもあります。

このテストケースは、SpotBugsが出しているtest-harnessモジュールを使っています*1SpotBugsRuleというクラスがキモで、これを使って実際の.classファイルを解析し結果を確認できます。assertion用のMatcherクラスも提供されているので、hamcrestを使ったテストも書きやすいです。

     @Rule
     public SpotBugsRule spotbugs = new SpotBugsRule();
     @Test
     public void testIssuesAreFound() {
         Path path = Paths.get("target/classes/my/company/AnalyzedClass.class")
         BugCollection bugCollection = spotbugs.performAnalysis(path);

         // There should only be exactly 1 issue of this type
         final BugInstanceMatcher bugTypeMatcher = new BugInstanceMatcherBuilder()
                 .bugType("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE").build();
         assertThat(bugCollection, containsExactly(bugTypeMatcher, 1));

         final BugInstanceMatcher bugInstanceMatcher = new BugInstanceMatcherBuilder()
                 .bugType("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
                 .inClass("AnalyzedClass")
                 .atLine(25)
                 .build();
         assertThat(bugCollection, hasItem(bugInstanceMatcher));
     }

以前はプラグインを実際に使ったテストをするためにはfindbugs-maven-pluginを使ってインテグレーションテストを回すしか方法がありませんでしたが、この手法ならより高速にテストを回せます。また test-driven-detectors4findbugsと違って公式なので、今後のSpotBugsの更新にも追随できるはずです*2

メタデータを作成する

まずbugrank.txtは現状必要ないようです。無くてもプラグインはビルドできます。そもそもBugRankという概念はこの論文でやこのページ紹介されている通り、SonarQubeのSeverityのようなもので、priority (confidence)とは全く別の概念です。が、果たしてそれがどの程度浸透しているのか……。SpotBugsが使うpriorityやpriority (confidence)のことは忘れてSonarQubeのSeverityだけ使うのが、運用も回しやすいと思います。

ので、全体的な情報を提供するfindbugs.xmlと、ユーザ向けメッセージを管理するmessages.xmlだけ作成すれば十分です。プロジェクトの雛形にはこれらが既に作成されているので、それを参考にコピペで要素を増やしてください。

番外編:SonarQubeプラグインを作成する

SonarQubeプラグインsonar-packaging-maven-plugin を使ってパッケージできます。sonar-findbugsと拙作findbugs-slf4jが参考になります。

キモはfindbugs-pluginバージョン3.5への依存を宣言することです。このバージョンはSpotBugs 3.1.0-RC2を使っているため、SpotBugsに依存したプラグインを書くには必須です。

なおsonar-findbugsバージョン3.5はpre-releaseバージョンで、まだSonarQubeのUpdate Centerでは配布されていません。使用する際はGitHubのリリースページからjarを落としてきて手動インストールする必要があります。

またSonarQube用のrule.xmlを生成する必要があるのですが、これは雑ですがMavenプラグインを作ったので使ってもらえればと思います。Groovy好きな方はsonar-findbugsのコードを参照しても良いでしょう。

まとめ

以上でSpotBugsプラグインを実装してSonarQubeも含めた運用を回すことができるはずです。 まだ各種親Detectorを学習する手間は必要ですが、5年前に比べてだいぶマシにはなりました。

追記:公式かつ英語のドキュメントもできました。

*1:find-sec-bugsからコードを持ってきて作られたもの

*2:test-driven-detectors4findbugsはFindBugs 1.3.9〜2.0.2にしか対応していなかった

JJUG CCC 2017 Springでセッション&LT参加してきました

前回のエントリでの宣言通り、本日のJJUG CCCでセッション発表してきました。発表のスライドと動画が後日、下記会社のアカウントから共有されると思います。公開されたらこの記事で紹介します。

昼休み明けすぐの発表でしたが、だいたい140名ほど参加いただけたようです。全日程では1,032名の参加があったそうですが、セッション発表時点では700名近く参加されていたらしいので、全体の2割近い方が参加されていたことになりますでしょうか。FindBugs知名度を改めて感じました。

静的解析ツール初心者の聴講者はゼロだったので、最初の方の静的解析ツールのメリットについてはかなり端折り、突っ込んだ内容を厚めに話しました。プラグイン開発デモができなかったのは残念でしたが、spotbugs-archetypeを使っていただければある程度は伝わる……といいなぁと思います。

また懇親会でのLTでも2分ほど話してきました。こちらは純粋にSpotBugsの中の人としての参加です。SpotBugs 3.1.0 RC2はわりと安定していますので、皆さんも使えるところで試してフィードバックいただければと思います。

追記(2017/05/24)

セッションのスライドが公開されました。

www.slideshare.net

JJUG CCC 2017 SpringにてSpotBugsの話をします

最近ブログ熱が下がっていたのと、SpotBugsの中の人になってドキュメントとかいろいろ触っていたのとで更新が途絶えていました。 そろそろSpotBugs 3.1.0−RC2を出せるのではと思いますので、そのうち試してください。
なおJava8対応強化だけであればリリース済みのRC1でも試せます。RC2ではEclipse Plugin周りの強化が入る予定です。

さてFindBugsのフォークであるSpotBugsについて、今週土曜日に開催されるJJUG CCC 2017 Springでセッションをします。

勤め先がスポンサーをしている縁での参加となるため、SpotBugsの中の人としてのPRだけでなく、SpotBugsを業務利用している立場としての工夫や注意点、運用についても話す予定です。日本での発表はかなり久々ですが、面白い話ができればと思います。

ご参考:過去の発表内容

お前誰?という方のために、過去の発表資料を載せておきます。最近やっているのはJavaJavaScriptとDockerとMSAが多いです。

FindBugsコミュニティにおける例の件の顛末、そしてSpotBugsとは何か

先週、FindBugsのメーリスに興味深い宣言が流れました。今のFindBugsはメンテ困難であり現体制での継続保守が難しいとするものです。

 ただこれをもってFindBugsはおしまいだ、としてしまうのはちょっと違います。コミュニティ自体はまだ活発で、プロジェクトのあるべき姿について話し合っています。私はコミッタでもなんでもないのですが、1週間経ったこともあり日本語で簡単に経緯を紹介したいと思います。

出典明示のため細かくリンクを張りますが、リンク先を見ずとも本エントリだけで理解できるよう努めます。

 

現体制最大の問題はプロジェクトリーダーの不在

1年以上前から、プロジェクトリーダーのBill Pugh氏はコミュニティ開発者からの連絡に返信しなくなりました。メールもそうですがGitHubTwitterでのmentionにも返信しません。私もこの5月にTwitterで連絡を取ろうとしましたがやはり返信はありませんでした。

彼だけがSourceForgeGitHubの管理者権限を持っているので、彼の協力なしにはウェブサイトの更新も新バージョンのリリースもTravis CIの導入もできません。これがプロジェクトを停滞させている最大の問題です。

 

なお上記のメールが流れた今でも返信がなく、Hacker Newsに「1週間時間をくれ」と一方的に声明を出しただけです。そしてその1週間が経過した今でも連絡はありません。コミュニティオフィシャルの場に現れないこの姿勢は、本当に戻ってくるつもりがあるのか不安にさせるものです。

 

分断を最大限防ごうとしているコミュニティ

このような状態ではプロジェクトのフォークが検討されると思いますが、上記メールではフォークは推奨されていません。確かにSpotBugsというフォークがありますが、これはFindBugsと決別したものではなく、プロジェクトリーダー不在でも開発を続けるための最後の手段です。今後のプロジェクトリーダーの行動次第では、FindBugsにマージされる可能性があります。

FindBugsにはプラグイン機構があり、その上にfb-contribfind-sec-bugsなどの素晴らしいプロダクトが作られています。またビルドツールやIDEとの高い親和性もあり、幅広いユーザに利用されてもいます。互換を考慮した上でJava9対応などの新機能を入れ、バグやドキュメントの不備を直していければ理想的です。

 

以上のように、現行のFindBugsユーザはまだ静観して構わない状況と考えます。このままSpotBugsがFindBugsにマージされなくても、JARファイルの名前が変わる程度の影響で当面は利用できるはずです。むしろドキュメントの不備が直りやすくなったり、修正リリースがより頻繁になったりと恩恵のほうが大きいのではないでしょうか。

 

FindBugs以外のバイトコード静的解析手段を検討したい場合

それでもFindBugs以外の選択肢を検討したい場合は、当該スレッドで言及されたHuntBugsを試してみてください。FindBugsの機能の40%がカバーされているそうです。もちろんHuntBugsには別の課題があることには留意してください。

他にはIntelliJ IDEAも推奨されています。IDEAはIDEですが、バッチ(CI)に組み込んでXMLを出力させることも可能とのことです。

また個人的にはGoogleの開発しているError Proneに期待しています。Eclipse統合はだいぶ難しそうですが、動作が軽いですしテスタブルなプラグイン開発が可能です。Google社内でも使われているそうですし、今後しばらくは改善されていくと期待できます。

 

まとめ

以上、 簡単に経緯と現状をまとめました。

コントリビュートに関心のある方は、FindBugsではなくSpotBugsのリポジトリをご覧ください。ドキュメントを更新するなかで、英語→日本語の翻訳は今後求められるはずです。単にFindBugsを使いたいという方は、ひとまず従来通りで大丈夫です。

MSAキャッチアップ用プロジェクトを作りました

MSAは理屈が多く、手を動かさないとわからないことが多すぎるので作りました。

ついでに非同期I/OとサーバサイドReactiveを学ぶためにVert.xRxJavaを採用しています。Spring Framework 5は別で学ぶ機会があるので一旦除外。ひとまず試行錯誤できる環境が用意できた状態です。

できていること

  • マイクロサービスごとに独立したMavenプロジェクト
  • docker-composeですべてのマイクロサービスを起動・リンクしての動作確認
  • docker-compose scaleでのマイクロサービスごとのスケールイン・スケールアウト
  • 非同期I/O主体のWEBサーバ開発
  • マイクロサービス間のHTTP通信
  • フロントエンド(UI生成とリクエスト受け付け)とバックエンド(FFmpegによる変換)の分離
  • マイクロサービスごとに個別のDB(postgres)を用意
  • Hashicorp Consulによるサービスディスカバリ
  • Hazelcastをメッセージキューとしたマイクロサービス間でのイベントのやり取り
  • nginx-proxyによるAPサーバ数増減への追随

認識できている課題

  • 共通ライブラリ(common)がある。メッセージキューに突っ込むエンティティ(イベント)クラスを共有したり、UUID生成方法を統一したりするためだが、各マイクロサービスがライブラリのバージョンに対するコントロールを失う可能性がある。エンティティはJsonObjectのような汎用クラスで置き換えるべきかも。
  • マイクロサービス間の通信に非効率なJSON&REST on HTTPを使っている。HTTP/2を入れたりgRPCに切り替えることでどの程度性能に影響があるのか見てみたい。
  • メッセージキュー(Vert.xデフォルトのEvent Bus)がメッセージの伝達を保障しない。解消のために使い慣れたApache Kafkaに置き換えるか、Yahoo! Pulsarを試す機会にしたい。
  • メッセージキューがグループをサポートしない。1つのイベントを複数の異なる種類のコンシューマが受け取るという、コレオグラフィ採用においてわりと重要なことがやりにくい。上述のKafkaかPulsarで解消できる。
  • サーキットブレーカが無い。対象マイクロサービスが動いていないと、サービスディスカバリがNPEで落ちる。Vert.xがコンポーネントを提供しているが、インタフェースがいまいちっぽい。
  • ユーザ認証がない。OAuthくらいは組み込みたい。Vert.xがコンポーネントを提供している
  • 監視の経験を積みたい。Vert.xはHawkular連携用のコンポーネントを提供している
  • REST APIのバージョン管理。HTTPヘッダかURLでバージョンを指定する手法が採れる。
  • 本当にReactiveの利点が出ているか確信がない。特にback pressureについてはできていないのでは、Pumpだけでファイル-HTTP間ないしHTTP-HTTP間のback pressureが実現できているのだろうか。