2013年3月14日木曜日

Play Framework2.0のプロジェクトに対してJenkins上でFindbugsを実行する方法

Play Frameworkのプロジェクトに対して、継続的にFindbugsを実行して安定してバグのない状態でリリースをしたい。

Play Framework1系の頃はwarにするプラグインとかもあったので、mavenやらantやらが使えたんじゃないかと推測していますが、とりあえずPlay Framework2.0で mvn installとか試しても駄目ぽかったので、FindbugsはEclipseで実行するしか無い!?なんて思っておりました。

でも今時のフレームワークでそんなわけない、と思い直し、調べてみたら、JenkinsさんでFindbugs実行できました。

今回動作を確認したのは

Play Framework2.0.4 Java版

で作られたプロジェクトです。

主に参考にしたのは下記サイトです。
http://stackoverflow.com/questions/13930791/using-findbugs-with-play-framework-2
http://www.inoio.de/blog/2013/01/08/integrating-findbugs-into-a-playframework-2-project-java/

プロジェクト側の準備

Playのアプリケーション側で変更を行うファイルは2つだけ。

  • project/plugins.sbt
  • project/Build.scala

になります。

project/plugins.sbt
// Comment to get more information during initialization
logLevel := Level.Warn

// The Typesafe repository
resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"

// Use the Play sbt plugin for Play projects
addSbtPlugin("play" % "sbt-plugin" % "2.0.4")
に対して、次の1行を追加します。
addSbtPlugin("de.johoop" % "findbugs4sbt" % "1.1.7")
ちなみになぜか
addSbtPlugin("play" % "sbt-plugin" % "2.0.4")
 の次の行に書いたらビルドの時に怒られたので、1行あけた上で追加しました。

最終的にはこうなりました。


// Comment to get more information during initialization
logLevel := Level.Warn

// The Typesafe repository
resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"

// Use the Play sbt plugin for Play projects
addSbtPlugin("play" % "sbt-plugin" % "2.0.4")

addSbtPlugin("de.johoop" % "findbugs4sbt" % "1.1.7")

project/Build.scala
import sbt._
import Keys._
import PlayProject._

object ApplicationBuild extends Build {

  val appName = "hoge"
  val appVersion = "0.1"

  val appDependencies = Seq(
    // Add your project dependencies here,
    "org.apache.commons" % "commons-email" % "1.2",
    "commons-io" % "commons-io" % "2.0.1",
    "commons-daemon" % "commons-daemon" % "1.0.10",
    "commons-lang" % "commons-lang" % "2.6",
    "commons-dbutils" % "commons-dbutils" % "1.5",
    "commons-codec" % "commons-codec" % "1.6",
    "mysql" % "mysql-connector-java" % "5.1.20",
    "commons-beanutils" % "commons-beanutils" % "1.8.3"
    )

  val main = PlayProject(appName, appVersion, appDependencies, mainLang = JAVA).settings( // Add your own project settings here
    resolvers += "Seasar Maven Repository" at "http://maven.seasar.org/maven2/")
}
みたいなところに1行import文を追加します。

import de.johoop.findbugs4sbt.FindBugs._

あとはval mainの箇所をこんなかんじで書き換えます。
val main = PlayProject(appName, appVersion, appDependencies, mainLang = JAVA,settings = Defaults.defaultSettings ++ findbugsSettings).settings( // Add your own project settings here
    resolvers += "Seasar Maven Repository" at "http://maven.seasar.org/maven2/")
}
以下を追加するような感じですね。引数でsettingsを追加して、さらに.settingsがあるのがなんか解せない感じですが、とりあえずスルーです。

settings = Defaults.defaultSettings ++ findbugsSettings 
とりあえずこれで動きはしますが、Findbugsの除外ルールを書いておきましょう
val main = PlayProject(appName, appVersion, appDependencies, mainLang = JAVA,
settings = Defaults.defaultSettings ++ findbugsSettings).settings(
// Add your own project settings here
    resolvers += "Seasar Maven Repository" at "http://maven.seasar.org/maven2/",
  findbugsExcludeFilters := Some(
    <FindBugsFilter>
      <Match>
        <Class name="~views\.html\..*"/>
      </Match>
      <Match>
        <Class name="~Routes.*"/>
      </Match>
      <Match>
        <Class name="~controllers\.routes.*"/>
      </Match>
    </FindBugsFilter>
  )
) 
ここでは findbugsExcludeFilters を指定しましたが、他にもいくつか指定できる項目があるようです。

https://bitbucket.org/jmhofer/findbugs4sbt/wiki/Home
に載っている項目が指定できるようなのですが、

  • findbugsReportType
  • findbugsEffort

などを指定しようと試みたところ、エラーになりました。

最終的にはこうなりました。


import sbt._
import Keys._
import PlayProject._
import de.johoop.findbugs4sbt.FindBugs._

object ApplicationBuild extends Build {

  val appName = "hoge"
  val appVersion = "0.1"

  val appDependencies = Seq(
    // Add your project dependencies here,
    "org.apache.commons" % "commons-email" % "1.2",
    "commons-io" % "commons-io" % "2.0.1",
    "commons-daemon" % "commons-daemon" % "1.0.10",
    "commons-lang" % "commons-lang" % "2.6",
    "commons-dbutils" % "commons-dbutils" % "1.5",
    "commons-codec" % "commons-codec" % "1.6",
    "mysql" % "mysql-connector-java" % "5.1.20",
    "commons-beanutils" % "commons-beanutils" % "1.8.3")

  val main = PlayProject(appName, appVersion, appDependencies, mainLang = JAVA,
    settings = Defaults.defaultSettings ++ findbugsSettings).settings( // Add your own project settings here
      resolvers += "Seasar Maven Repository" at "http://maven.seasar.org/maven2/",
      findbugsExcludeFilters := Some(
        <FindBugsFilter>
          <Match>
            <Bug category="MALICIOUS_CODE"/>
          </Match>
          <Match>
            <Bug category="NOISE"/>
          </Match>
          <Match>
            <Bug category="I18N"/>
          </Match>
          <Match>
            <Bug category="SECURITY"/>
          </Match>
          <Match>
            <Bug category="EXPERIMENTAL"/>
          </Match>
          <Match>
            <Bug pattern="BAC_BAD_APPLET_CONSTRUCTOR"/>
          </Match>
          <Match>
            <Bug pattern="DMI_UNSUPPORTED_METHOD"/>
          </Match>
          <Match>
            <Bug pattern="CD_CIRCULAR_DEPENDENCY"/>
          </Match>
          <Match>
            <Bug pattern="IMA_INEFFICIENT_MEMBER_ACCESS"/>
          </Match>
          <Match>
            <Bug pattern="IL_INFINITE_RECURSIVE_LOOP"/>
          </Match>
          <Match>
            <Bug pattern="PS_PUBLIC_SEMAPHORES"/>
          </Match>
          <Match>
            <Bug pattern="UOE_USE_OBJECT_EQUALS"/>
          </Match>
          <Match>
            <Bug pattern="USM_USELESS_SUBCLASS_METHOD"/>
          </Match>
          <Match>
            <Class name="‾views¥.html¥..*"/>
          </Match>
          <Match>
            <Class name="‾Routes.*"/>
          </Match>
          <Match>
            <Class name="‾controllers¥.routes.*"/>
          </Match>
        </FindBugsFilter>))
}


ではまずここまででfindbugsを実行してみましょう。

http://www.inoio.de/blog/2013/01/08/integrating-findbugs-into-a-playframework-2-project-java/

には

sbt findbugs

のコマンドが書いてありましたが、playコマンドで実行すれば問題ありません。
とりあえずplayのコンソールを開きます。(下の画面のバージョンが2.1.0になってるのは一旦無視して下さい)

       _            _
 _ __ | | __ _ _  _| |
| '_ \| |/ _' | || |_|
|  __/|_|\____|\__ (_)
|_|            |__/

play! 2.1.0 (using Java 1.6.0_37 and Scala 2.10.0), http://www.playframework.org

> Type "help play" or "license" for more information.
> Type "exit" or use Ctrl+D to leave this console.

[hoge] $ 
ここで次のコマンドを実行します。

       _            _
 _ __ | | __ _ _  _| |
| '_ \| |/ _' | || |_|
|  __/|_|\____|\__ (_)
|_|            |__/

play! 2.1.0 (using Java 1.6.0_37 and Scala 2.10.0), http://www.playframework.org

> Type "help play" or "license" for more information.
> Type "exit" or use Ctrl+D to leave this console.

[hoge] $ findbugs

これで実行されてtarget/scala-2.9.1/findbugs/findbugs.xmlが生成されます。

ちなみに、project/Build.scalaに除外ルールを記述したと思いますが、除外ルールを変更しても全然更新されませんでした。
cleanしても駄目だなあと思いましたが、次のコマンドを打てば除外ルールがクリアされます。




       _            _
 _ __ | | __ _ _  _| |
| '_ \| |/ _' | || |_|
|  __/|_|\____|\__ (_)
|_|            |__/

play! 2.1.0 (using Java 1.6.0_37 and Scala 2.10.0), http://www.playframework.org

> Type "help play" or "license" for more information.
> Type "exit" or use Ctrl+D to leave this console.

[hoge] $ update


どうやら一つ上のレイヤーで更新をかけて上げる場合にはcleanコマンドではなく、updateコマンドを使うようです。


Jenkins側の準備

Jenkinsはすでに稼働しているものとします。
そして、とりあえずPlay Frameworkのプロジェクトに対してJenkins側でジョブを作って、distコマンドとか実行して、playのstart,stopが出来ている状態を前提としています。

プラグインのインストール

必要になるのは次のプラグインです。

  • sbtプラグイン(多分これなくても動かせます)
  • findbugsプラグイン
まあ、サクッと入れてあげましょう。
ちなみにsbtコマンドが必要、つまりCentOSだと
yum install sbt
が必要?とか思いましたが、不要です。

以下、多分不要です。
################ここから#######################
さてプラグインを入れたら1つだけ設定を行います。

「Jenkinsの管理」→「システムの設定」からsbtのjarファイルのパスを指定してあげます。

今回は次のようにしました。

Name:play2.0.4
Path:/usr/local/play-2.0.4/framework/sbt/sbt-launch.jar

Playの中に含まれているsbt-launch.jarを指定してあげればOKです。
################ここまで#######################

ジョブの作成

ビルドでまずはシェルの実行をします。
PLAY_HOME=/usr/local/play-2.0.4

cd ${WORKSPACE}
${PLAY_HOME}/play clean
${PLAY_HOME}/play compile
とりあえず上のように設定しましたがどちらかと言うとupdateコマンドを呼んであげたほうがいいかもしれません。

こうすれば多分sbtプラグインがなくてもfindbugs実行出来ます。

PLAY_HOME=/usr/local/play-2.0.4

cd ${WORKSPACE}
${PLAY_HOME}/play clean
${PLAY_HOME}/play update
${PLAY_HOME}/play compile
${PLAY_HOME}/play findbugs

以下、多分不要です。
################ここから#######################
次にBuild using sbtを選択します。
変更する場所としては、Actionsに以下を指定します。

Actions:findbugs

################ここまで#######################


ビルド後の処理として、
Findbugs警告の集計を追加します。
もともとfindbugs.xmlを生成するようにしてあるので、特に設定を変更せずに行けました。

これでビルドを実行することでFindbugs警告をJenkins上で確認できるようになります。

xmlによる出力だと人間の目で見ても見づらいですが、Jenkins上だと非常に見やすくなりますね。

2013年3月12日火曜日

「(CakePHPとか)PHPのテストについての勉強会」を横で聞きながらの感想

先日、Co-Edoに遊びに行きました。
その日はCakePHPとか、PHPのテスト勉強会をやっていました。

部屋の片隅でコソッとJavaをいじりながら聞いていた感想を勝手に書きたいと思います。

以前はSymfonyやCakePHPなどをやっていましたが、最近はJavaしか触っていないので、そこまで深くは聞いていなかったのですが、どの言語でもやることは一緒だなあと思ったので、PHPのテストの話を聞いて、Javaでどこまで実践できているかを考えてみたいと思います。

テストの基本からCakePHPでTDD/BDDへ by @sizuhiko


テストを書きましょう、という話です。
テストを書くことによるメリットの説明などが行われていました。CGI時代は単純なプログラムだったけれど、現在は複数人での開発、プログラムの複雑化などにより、テストの必要性が高まっているということでした。
スローテストは不健康の始まり、ということで、テストを走らせてから30分もテストしっぱなしだとだめだよね、という話でした。
テスト自体もCIしなきゃ駄目ですね。
さらにはCakePHPのテストについての説明でした。
CakePHPのテストでSelenium2が使えます。Jenkinsで継続的にテストしましょうという話でした。

【ふりかえり】テストはその昔、Slim3のチュートリアルでテストを書いたことがあるだけです。。。


「CakePHP2+Jenkinsで継続的インテグレーション」について話してきました


Jenkinsを使ってCakePHP2をテストする方法の説明でした。

CakePHP2用のBuildファイル サンプル(Ant)も用意されているようでございます。
【ふりかえり】PHPでもCheckstyleやPMD使えるんですね。Jenkinsはビルド以外にはFindbugsなどで使ってますが、テストを実行させたことはありませんでした。

Play FrameworkのBuildファイルサンプル、どこかにないかなあ。。。


xdebugリモートデバッグ


xdebugを使ってEclipseでデバッグする方法の説明でした。
PHPだと、print_r使ってデバックすることが多かったです。
EclipseでSSHトンネル掘ってデバックする方法も紹介されていました。

【ふりかえり】JavaだとEclipseの強力なデバックツールに助けられますね。Play Frameworkだとデバックモードが重いので、気が重いですが。。。
SSHトンネル掘ってのデバックということは、例えばJenkinsサーバ上とか検証環境で動かしているのもデバック出来るということだろうか?今度やってみよう。

PHPUnit と Selenium2 を使ってブラウザベースの自動テストを実行するための最初の一歩的な何かを発表してきた


PHPでSelenium2を使う方法の紹介でした。
やはりシナリオ作成はFirefoxのプラグインでボタンポチポチして、ソースコードに変換する方法ですね。
IEだとちょっと設定が色々必要なようです。

【ふりかえり】とりあえずやったことは、ある。サーバの不可などでWaitの制御が結構手間かかる。

IntelliJ IDEA からCakePHPのテストを 実行する方法


 IntelliJ IDEAでテストを動かす方法の紹介でした。

 【ふりかえり】IntelliJ IDEA =PhpStormなんでしょうか?最近すごいよく聞きます。
NetBeansやらAptanaやらに心変わりしたこともありますが、結局Eclipseに戻ってきたので、もう他のエディタに移る気はあんまりありません。WebStormを使ったときはJavascriptの強力なエディタだなあとは思いましたが。。。


ちなみに、今回新しいサイトを立ち上げようとしていて、言語・フレームワーク選定から入りました。近々リリースします。

JavaかPHPを使うとしたら今の時代、個人でなるべくお金をかけずにやるなら以下の選択肢かと思います。

(なお、Ruby,Pythonは1から勉強しないといけないため、今回は選定から外しました。
諸般の事情で早いところサイトを立ち上げて、というか、復旧させて、ドメインに良い感じのコンテンツを置いてあげないといけない状況になっているので、とにかくスピード重視です。この辺りの諸般の事情については、また別のエントリーで触れたいと思います。)

PHPのフレームワーク(CakePHP,Symfony)×ロリポサーバ

まあ、Symfonyは2系になってから大規模過ぎて挫折した経験があるので、CakePHPが手っ取り早いと思います。

Play Framework×Heroku

今の時代、PaasといったらHerokuでしょうか。ただ、無料で使える範囲だとDBサーバーの容量がすぐにあふれてしまいそうなので、今回は却下です。
Heroku以外でサクッとPlay Frameworkが動かせるPaasがあれば教えて欲しいです。

Slim3×GAE

GAEはオワコンでしょうか?正直そう思います。
GAEのDBサーバはダンプを取ってMySQLに移行とかがサクッと出来ません。一生添い遂げる覚悟がないと厳しいかもしれません。
GAEでもRDS系のDBが使えるようですが、本気で有料のようです。
今回たちあげたいサービスはYoutubeからデータを引っ張ってくるので、同じGoogleのサービスであれば、親和性が高いかも、と雰囲気で選びました。
親和性云々実際は関係無さそうでした。
jarファイルもXXX-appengine.jarみたいなのと、どっち使えばいいの?という感じです。

結局慣れたところでSlim3を使うことにしました。
今からCakePHPを思い出すのはちょっと時間かかりそうだった、というのとGAEの限界とHerokuの限界だったら、Herokuの限界の方が先にきそうだと思ったので、GAEとしました。