Subscribed unsubscribe Subscribe Subscribe

Kengo's blog

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

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

FindBugs error-prone

先週、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キャッチアップ用プロジェクトを作りました

Docker docker-compose java original ReactiveX vert.x プログラミング

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が実現できているのだろうか。

RxJavaはVert.x 3でSQL書くのに便利

java vert.x プログラミング ReactiveX

自分にとっての新技術を試すための個人プロジェクトでVert.xを使っているのだが、PostgresクライアントがTechnical Previewになったのでオンメモリに乗せていたデータをpostgresに乗せるようにしたところ、2重3重のネストがコードに現れるようになった。

そこでRxJavaを利用することでコードの短縮化を図った。以前利用したReactive Streamsとは別物とのこと。

結果

Handlerベースの実装だとClassがだいたい100行だが、Observableベースの実装だと40行強で済んだ。ネスト数も抑えられている。

何が効果的だったのか

コードの見通しを良くしたのは例外処理。 Handler (コールバック)内で処理が失敗したかどうか毎回確認していたのが、Stream API同様のmap処理に置き換えられた。

サービスを書いているとよくある「getConnectionしてトランザクションを開始してSELECTしてINSERTしてCOMMITする」という処理が、Handlerで書くと5重ネストと5回の例外処理に化けてしまう。ネストをメソッドチェーンに置き換えたうえで例外処理を一本化(主にSubscriber側の処理になる)できるのは非常に見通しが良い。

// before
        postgreSQLClient.getConnection(connected -> {
            if (connected.failed()) {
                future.fail(connected.cause());
                handler.handle(future);
            } else {
                SQLConnection con = connected.result();
// after
        return postgreSQLClient.getConnectionObservable()
            .flatMap(con -> {

使用感:DB操作をObservableとしてを扱うコードを書く上での課題

postgresクライアントのInstantの扱いにバグがある模様。現時点ではStringとして取り出す必要がある。Technical Previewだし仕方ない。

非同期APIなのでリソースの解放にtry-with-resourcesやLoanパターンが使えない点に注意が必要。例えばDB接続を使い終わった時点で接続を閉じるには、以下のように Observable#doAfterTerminate(Action0)を使う必要がある。これなら処理が正常終了した時も異常終了した時も実行される。

        return postgreSQLClient.getConnectionObservable().flatMap(con -> {
            return con.queryObservable(
                    "SELECT id, uploaded_file_name, resolutions, generated FROM task")
                    .doAfterTerminate(con::close);
        }).flatMap(selected -> {

なおリソース解放用に用意されているObservable#using()を律儀に使おうとすると以下のようになってしまい、EclipseJavaコンパイラだとうまくコンパイルできないことがある。第一引数にFunction0(Resource)ではなくFunction0(Observable<Resource>)を使えるなら、() -> conなどという無意味なコードを書かなくて済んだのだが……。

実装を見るとusing()doAfterTerminate()とは全く違う複雑な処理をしているので、doAfterTerminate()で完全な代替になるとは思わないほうが良さそうだ。Observable#using()を自然に使えるような書き方を探す必要があるが、Vert.xのサンプルプロジェクトには現状usingを使ったサンプルは無い。

        return postgreSQLClient.getConnectionObservable().flatMap(con -> {
                return Observable.using(
                        () -> con,
                        connection -> connection.queryWithParamsObservable("SELECT uploaded_file_name, resolutions, generated FROM task WHERE id = ?", params),
                        connection -> connection.close());
        }).flatMap(selected -> {

もっと利用できそうな機能

今のところ更新系など「値を返す必要はないけど正常終了したかどうかだけ確認したい」場合にObservable<Void>を返しているが、Single<Void>を使うほうがシンプルかもしれない。複数の結果が帰ってくることがないとわかっている以上、それを型として明示したほうが何かと混乱がなさそう。

現在のプロジェクトではSchedulerを一切使っていないが、Vert.xと組み合わせて使う以上その必要性は薄そうなので、GUIアプリで試す必要があるかも。

まとめ:RxJavaは何に便利か?

自分が想像できるのは非同期I/Oを多く持つサーバで、多数のI/Oをマージしてクライアントにレスポンスを返すような処理を書くこと。もちろんブロッキングI/Oも混ぜることも可能(Vert.xの機能に寄せるかSchedulerを利用する)。Stream APIとほぼ同じ感覚で扱える。

またObserverSubscriber利用するスレッドを指定できるので、UI Threadを極力あけたい&イベントベースで書きたいAndroidのようなGUIアプリにおいても活躍が期待されているように見える。もうずっとSwingに触れていないがおそらくSwingでも使えるだろうし、JavaFxバインディングは公式に存在するようだ。

Impact of error-prone

FindBugs java error-prone

Findbugs is really good solution to improve quality of code

As an author of findbugs-slf4j, I really love Findbugs. It lets us find problems by static bytecode analysis. This tool improves quality of each module, reduce reproducing known bug, and point problem quickly in IDE. We do not have to use code review to find known problems and/or antipatterns, Findbugs will do it before developer sends pull-request. This 'push notification' is really cool, we can point problem even if developer has no motivation to find it.

It is really ideal solution also for large scale development team. Even if your team has hundreds developers, they can get help from Findbugs from the beginning of working day. Even though some of them have less experience and knowledge about our depending framework, module and language, Findbugs can help them to catch up quickly.

Findbugs is constructed based on both academic and practical experience, and now it's OSS so not only limited contributors but also everyone who wants to improve documentation comment, message translation, new detectors and so on.

Problems in Findbugs

But I have a dream to implement Findbugs alternative, to replace Findbugs with better implementation. In my personal opinion, it has some problems:

  • Difficult to maintain: Less documentation comment to describe usage*1, Less fresh document to describe (e.g. how to implement plugin), Too wide scope,
  • Slow performance: Not ready to use multi-thread so efficiently. No support for incremental analysis provided by build tools.
  • Difficult to test: To test Findbugs plugin, test-driven-detectors4findbugs was good solution but it doesn't work with Findbugs 3.0.1 due to internal implementation change. We also need a hack to test by Maven.

Yes Findbugs is OSS, so in theory we can solve them by own. But recently this project is not so active, and there is no popular fork for now. It would be problem to solve.

New alternative: error-prone

Last week I found an interesting static analysis tool, named error-prone.

github.com

This does not check bytecode, it applies hack to javac so it handles AST under com.sun.source.tree package. In my understanding, it has many good points:

  • Providing automated correction.
  • Providing simple but enough guideline, so we can write unit test to test rules to detect bug pattern.
  • It's product from Google so it has detailed documentation comment.
  • Using Annotation Processor to manage metadata of bug pattern, so we do not have to maintain XML files,

I haven't tried it with large amount of code, so I cannot compare its performance with Findbugs yet. I hope it's better than Findbugs. About openness and activeness in its community, I'm not sure yet. I've sent a pull-request to add bug pattern, I hope it's open even to non Google developers.

Do we need to switch to error-prone?

In my understanding, problem in error-prone is as follows:

So if you're using Findbugs, I think you do not have to switch to error-prone for now, its merit is really limited and almost all of them are not for users but for contributors.

But if you're author of Findbugs plugin, I think error-prone has value to keep watching on it. If its agility and resource is enough, it might grow and replace other solution later.

*1:I've sent some patches and pulll-requests but it's not enough yet

エラーログと例外の扱いについて

SLF4J java management

社内勉強会向けに作成したエラーログと例外の扱いについて説明したスライドを、Speakerdeckで公開しました。一部社内特化部分(ログレベルポリシーやMSA向けMDC使用法 など)を削除して、口頭で補足した部分を補っています。

これらの理想的な使い方はシステムを設計から運用までひととおり経験してみないとわかりにくいものですが、それなりの規模のシステム企業における業務開発でその経験を得ることはかなり難しいのではないでしょうか。結果、何の意味もなさないログが量産されてシステム運用の邪魔になってしまいます。きちんとポリシーを組んで統一していくか、各開発者が運用を知り納得して良いログを書けるようにしていく必要があります。

マネジメントとは挑戦と成長のための環境づくりだと考えている私にとって、ポリシー策定よりは「きちんと運用を知り自発的に支えになれる開発者」になれるための情報伝達こそがManagerとしてやるべきだと考えており、そのためのアクションがこのスライドというわけです。前回もそうでしたが、しばらくは非機能要件について考えるきっかけと大まかな方向性を提示していければと思っています。

関連する投稿

docker-composeで開発環境を配布する

docker-compose Docker

上海Microsoftで行われたShanghai Docker MeetupのLTにて発表した、docker-composeを使って開発環境をContributorに対して配布するというユースケースに関するスライドをSpeaker Deckに公開しました。

Partake開発の際はVagrantで開発環境を配布していたのですが、今ならdocker-composeの方がシンプルかつ幅広く使われているのではと思います*1。中国からだとToolboxのダウンロードやDocker registryからのダウンロードが安定しないという問題があるのですが、中国Microsoftでミラー等の公開を検討しているそうです。

なおこのスライドを作るときにdocker-compose.ymlフォーマットのバージョン2が公開されている事に気づいたのですが、残念ながらnginx-proxyが未対応だったのでまだ自分のプロダクトでは使えていません。


追記:nginx-proxyがdocker-compose.ymlフォーマットのバージョン2に対応したそうです。

*1:VagrantからDockerを使う必要性はあまり感じていません

コード内コメントとJavadocの書き方

java

社内勉強会向けに作成したコード内コメントとJavadocの書き方についてのスライドを、Speaker DeckとSliDeckで公開しました。

このスライドは概要とメリットについてざっくり説明し理解と学習の動機を作ることを目的としていますが、これは日本ないし中国の大学ではコメントについてそこまで扱わないらしい*1という前提に基づいたものです。

ここに記載のない発展的な内容としては以下が挙げられますが、これらについて学ぶのはまず手を動かした後で良いと考えています。

プレゼン後に「publicでないクラスやメソッドJavadocを書くべきか?」という質問を受けましたが、私の答えは「書くべき」です。自分自身の理解を深め、備忘にもなり、将来のメンテナンスも助けてくれます。少しでも直感的でないコードがあるならば、時間を取って書きたいです。 まぁ書くべきものは厳密には対外的なAPI Specではなく、内部実装方針や歴史的経緯等になるかもしれませんので、Javadocを書くべきというよりは開発者向けドキュメントを書くべきと言うべきなのかもしれません。

また、ドキュメンテーションコメントはOSS contributionの良い題材になるとも話しました。typo修正くらいなら敷居は低いですし、多くのOSSでは喜ばれるはずです。自分の経験上、リジェクトされることはほとんどありませんでした:

なお自分のJavadocに関する理解は、Oracle公式ドキュメントに加えて「エンジニアのためのJavadoc再入門講座」に依るところが多いです。英語の解説書でこのくらい幅広く丁寧に説明したものが欲しい……。

エンジニアのためのJavadoc再入門講座 現場で使えるAPI仕様書の作り方

エンジニアのためのJavadoc再入門講座 現場で使えるAPI仕様書の作り方

*1:共同開発ないし保守については優先度が低い印象を受ける