01 Oct 2017, 08:43

Java開発でJenkinsを個人運用するためのメモ

はじめに

仕事でJava開発をすることになったのだけれども、

コードの品質をGUIで可視化すると便利だなと思って、

Jenkinsをつかってみることにしました。

あくまで可視化するためのグラフィカルな便利cronとしての役割のみ求めているので、複雑なことや難しいことはしません。

やりたいこと

  • gradle 連携
  • FindBugsの指摘を可視化
  • CheckStyleの指摘を可視化
  • ステップ数を数える
  • TODO 管理
  • spock テスト実行

version

  • Jenkins 2.73.1
  • StepCounter Plugin 2.0.0
  • FindBugs Plugin 4.71
  • CheckStyle Plug-in 3.49
  • Task Scanner Plug-in 4.52

Jenkinsのインストール

お試しなので、自PCのUbuntu 16.04 LTSにインストールします。手順は以下。

仕事では、CentOS 7.4にインストールします。

ポート番号の変更をします。デフォルトでは8080です。

/etc/default/jenkinsを編集して、12345とか適当なものに変更します。

- HTTP_PORT=8080
+ HTTP_PORT=12345

Jenkinsを再起動。

$ sudo systemctl daemon-reload
$ sudo service jenkins restart

http://localhost:12345 にアクセスして、画面が表示されることを確認。

手順にしたがって、初期設定をする。オススメプラグインをインストールする。

リポジトリをチェックアウトしてビルドしてみる

とりあえず以下のgithub repoを利用します。ビルドツールはgradleです。

設定手順

  • General -> プロジェクト名に “hello_jenkins”を入力
  • ソースコード管理 -> git にチェックして、リポジトリURLに https://github.com/tsu-nera/java_spock_playground.git を入力
  • ビルド -> ビルド手順の追加 -> Invoke gradle script を選択

手動でビルドを実行すると、githubからcloneして、gradleコマンドが叩かれる。

プラグインでやりたいことを実現

ステップ数を数える

以下のプラグインを追加。

  • StepCounter Plugin
  • 設定 -> ビルド後の処理 -> ビルド後の処理の追加を選択
  • Step Counter を選択
  • ファイルの種類: java,
  • 解析するファイルパターン **/*.java を入力

FindBugsの設定

以下のプラグインを追加。

  • FindBugs Plug-in
  • 設定 -> ビルド後の処理 -> ビルド後の処理の追加を選択
  • Findbugs警告の集計を選択
  • build/reports/findbugs/main.xmlを入力。
  • build.gradleに以下を追加
apply plugin: 'findbugs'

findbugs {
    reportsDir = file("./build/reports/findbugs")
    ignoreFailures = true
}

CheckStyleの設定

  • CheckStyle Plug-in
  • 設定 -> ビルド後の処理 -> ビルド後の処理の追加を選択
  • CheckStyle警告の集計を選択
  • build/reports/checkstyle/main.xmlを入力。
  • build.gradleに以下を追加
apply plugin: 'checkstyle'

checkstyle {
    reportsDir = file('./build/reports/checkstyle')
    configFile = file('./sun_checks.xml')
    ignoreFailures = true
}
  • ここから、checkstyleの設定ファイルを取得してプロジェクトルートに置く。 https://github.com/checkstyle/checkstyle/tree/master/src/main/resources
    • google_checks.xml
    • sun_checks.xml
  • 設定-> ビルド -> ビルド手順の追加 -> Invoke gradle script を選択
  • Use Gradle Wrapperを選択。Tasksのところに以下を入力。

    checkstyleMain findbugsMain

TODO を表示

  • TaskScanner Plug-in
  • 設定 -> ビルド後の処理 -> ビルド後の処理の追加を選択
  • 未解決タスクの集計を選択
  • 集計対象に **/*.javaを入力

spock テスト実行

  • 設定 -> ビルド後の処理 -> ビルド後の処理の追加を選択
  • JUnitテスト結果の集計を選択
  • テスト結果xmlに **/*Spec.xmlを入力
  • 設定-> ビルド -> ビルド手順の追加 -> Invoke gradle script を選択
  • Use Gradle Wrapperを選択。Tasksのところに以下を入力。

    clean test

ジョブの実行

エラーするので以下をプロジェクトルートで叩いた。

sudo gradle wrap

01 Oct 2017, 06:13

Java開発でIntelliJ IDEAにとりあえず入れたプラグインたちのメモ

はじめに

仕事でIntelliJ IDEAを使って Java開発することになったので、

とりあえず便利そうなプラグインをみつくろって入れてみたので、そのメモ。

IntelliJはデフォルトでいろんなプラグインがすでにインストールされているのが嬉しい。

プラグイン

静的解析

  • FindBugs-IDEA
  • CheckStyle-IDEA

FindBugsと CheckStyleは定番な静的解析ツールなので、とりあえず入れておく。

ちなみに、InteliJでは、Ctrl + Shift + Alt + L で コードのフォーマット整形をしてくれる。

テスト

Spock Frameworkを使うので、サポートツールを入れる。ハイライトするだけかな?

  • Spock Framework Enhancements

ステップカウント

進捗はステップ数で報告することになっているので、

ステップカウンタ(LOC)がIDEから使えると便利。

  • Statistics

日毎のステップ数が差分でわかればいいのだが、これはエクスポート機能がみつからない。

ステップ数については、別のツールを使ったほうがいいかな。Jenkinsをかませるか?

保管アクション

保管をトリガにして、いろいろな機能を動かす。

  • Save Action

設定 -> その他の設定 -> 保管アクションから各種設定を有効にする。

これはある意味、チーム開発しているときは、

勝手にいろいろな部分を修正してしまう恐れがあって危険だな。

Lombok

  • Lombok Plugin

LombokをIntelliJで補完したりするプラグイン。

Lombokについては、以下の記事を参照。

その他

あまり入れるようなプラグインはなかったな。オススメプラグインはない。

それはデフォルトでIntelliJ IDEAがとても便利ということだ。

思いついたら、追記するようにする。

01 Oct 2017, 03:56

タイタニックで決定木、交差検証、グリッドサーチ(Python)

はじめに

データサイエンティスト養成講座の第二回を受けてきました。

扱った内容は、

  • 決定木
  • クロスバリデーション
  • グリッドサーチ

講座では、Rを使うのだけれども、Pythonにもなれておきたいので、

講座の内容をPythonで復習しました。

ついでに、kaggleのタイタニック問題を決定木で解きました。

今回のコードは、githubにあげています。以下は、コードの抜粋です。

Pythonで決定木

Pythonで決定木を使うには、scikit-learnライブラリを使う。

from sklearn import tree
clf = tree.DecisionTreeClassifier(random_state=17)
clf.fit(X, y)

簡単!

クロスバリデーション with KFold

決定木は、max_depthパラメータを大きくすればするほど精度が上がっていくが、汎化性能が下がっていく。なので、クロスバリデーションという方法を使って、過学習が起こっていないかチェックすることが重要になる。

from sklearn.model_selection import KFold
from sklearn import metrics
from sklearn.metrics import accuracy_score
K = 5
kf = KFold(n_splits=K, shuffle=True, random_state=17)

score_train_tmp = 0
score_test_tmp = 0

for train_index, test_index in kf.split(X):
    X_train, X_test = X[train_index], X[test_index]
    y_train, y_test = y[train_index], y[test_index]

    # 構築データでモデル構築
    clf.fit(X_train, y_train)

    # 構築データの予測値
    pred_train = clf.predict(X_train)

    # 構築データのaccuracy
    auccuracy = accuracy_score(pred_train, y_train)

    #構築データのaccuracyを足していく
    score_train_tmp+=auccuracy

    #検証データの予測値
    pred_test = clf.predict(X_test)

    #検証データのaccuracy
    auccuracy = accuracy_score(pred_test, y_test)

    #検証データのaccuracyを足していく
    score_test_tmp+=auccuracy
score_train_tmp/K
0.82463676190176005

score_test_tmp/K
0.80247944259619608

構築データと検証データのスコアが近ければ、過学習が起こっていないと判断できる。これが乖離していると過学習が起こっているので、パラメータチューニングが必要。

グリッドサーチ

最適なパラメータをしらみつぶしにパラメータを組み合わせて探索していく方法をグリッドサーチという。普通はfor文を回してパラメータを変えてスコアを見ることで調整していく。しかし、scikit-learnには、GridSearchCVというグリッドサーチをするための専用のクラスが用意されている。

これをつかえば、煩わしいfor文のネストを書かずとも、複数パラメータに対して探索をすくことができる。

調べたいパラメータをまずは辞書で定義する。

from sklearn.model_selection import GridSearchCV

# use a full grid over all parameters
param_grid = {"max_depth": [2,4,6,8,10],
              "max_features": ['log2', 'sqrt','auto'],
              "min_samples_split": [2, 3, 5],
              "min_samples_leaf": [1,5,8],
              "criterion": ["gini", "entropy"]}

次に、GridSearchCVを読んで、グリッドサーチを実行する。

tree_grid = GridSearchCV(estimator=clf,
                 param_grid = param_grid,   
                 scoring="accuracy",  #metrics
                 cv = K,              #cross-validation
                 n_jobs =-1)          #number of core

tree_grid.fit(X,y) #fit

tree_grid_best = tree_grid.best_estimator_ #best estimator
print("Best Model Parameter: ",tree_grid.best_params_)
print("Best Model Score    : ",tree_grid.best_score_)
Best Model Parameter:  {'criterion': 'gini', 'max_depth': 6, 'max_features': 'log2', 'min_samples_leaf': 8, 'min_samples_split': 2}
Best Model Score    :  0.812570145903

便利だ。素晴らしい。

しかし、このパラメータチューニングした結果をサイトにて提出しても、大したスコアは出ない。これは、特徴量エンジニアリングがそもそもしょぼいという問題がある。

30 Sep 2017, 07:00

[CentOS] IntelliJ IDEA では ibus に不具合があり日本語入力できないのでfcitxをつかおう

はじめに

仕事でVirtualBox上にCentOSを立ち上げてそのなかにIntelliJ IDEAを入れたのだけれども、

なぜか日本語入力ができなかったので、その調査と解決方法について書く。

環境

  • CentOS 7.4
  • IBus 1.5.3
  • fcitx 4.2.8

結論

調べたら、IntelliJ IDEAで IBus 1.5.11 以前のバージョンは不具合があるそうだ。

こんなメッセージが起動時に出る。

IBus prior to 1.5.11 may cause input problems. See IDEA-78860 for details.

おそらく、Jetbeans製のPhpStormやPyCharmでもどうようかと。

stackoverflowの解決方法にしたがって、ibus の最新版をいれたのだがibusがそれでもうまく動かなかった。おかしいな。

fcitxをつかおう

Input Methodは ibusだけではない。fcitxがある!というわけで、ibusをアンインストールしてfcitxをインストールすることにした。

以下、インストール手順です。

参考にしたリンク

fcitxをインストール

$ sudo yum install epel-release
$ sudo yum install -y \
fcitx-data.noarch \
fcitx-devel.x86_64 \
fcitx-gtk2.x86_64 \
fcitx-gtk3.x86_64 \
fcitx-libs.x86_64 \
fcitx-pinyin.x86_64 \
fcitx-qt4.x86_64 \
fcitx-qt5-devel.x86_64 \
fcitx-qt5.x86_64 \
fcitx-qw.x86_64 \
fcitx-table.x86_64 \
fcitx-anthy.x86_64
fcitx.x86_64

$ wget ftp://ftp.pbone.net/mirror/archive.fedoraproject.org/fedora/linux/updates/19/x86_64/fcitx-configtool-0.4.7-1.fc19.x86_64.rpm && sudo yum localinstall fcitx-configtool-0.4.7-1.fc19.x86_64.rpm

ibusの削除

$ sudo killall ibus-daemon
$ sudo yum remove ibus

fcitxをシステムのIMに設定

$ gsettings set org.gnome.settings-daemon.plugins.keyboard active false
$ imsettings-swich fcitx

~/.bashrcに追記

export GTK_IM_MODULE=fcitx
export QT_IM_MODULE=fcitx
export XMODIFIERS="@im=fcitx"

インライン入力を有効にする

これで一応はfcitxで IntelliJ IDEAが使えるようになったが、

入力すると、変換候補が下の方に出てきてしまう。インライン入力ができるように設定する。

  • fcitx -> 設定 -> アドオン
  • 拡張のチェックボックスをマーク
  • 検索窓からximを検索。Fcitx XIM Frontendを選択して設定を押す。
  • On The Spotを使うにチェックを入れ「OK」を押す

おわりに

解決方法に至るまでに6時間かかった。

ニッチなトラブルシューティングだけれども、同じように困っている人の助けになれば幸いです。

24 Sep 2017, 13:39

Spock で モック を interfaceを用意せずにつくる方法

はじめに

前回、spockの mock 機能を利用するために、interfaceを用意していた。

というのも、interfaceからでないとMockを作成できないと思っていたからだ。

しかし、interfaceを用意しなくても、classからモックを作成できることがわかったので、紹介。

byte-buddy

class からモックを作ろうとすると、以下のようなエラーがでる。

org.spockframework.mock.CannotCreateMockException:Cannot create mock for class sample.Calculator. Mocking of non-interface types requires a code generation library. Please put byte-buddy-1.6.4 or cglib-nodep-3.2 or higher on the class path.

注目すべきは、Please put byte-buddy-1.6.4 or cglib-nodep-3.2 or higher on the class path.

なんだ、byte-buddyとは?ということで、検索。

どうやら、コードを自動生成するようなライブラリらしい。早速インストール。gradleをつかっているので、以下を build.gradleのdependencies

に追加。他のビルドツールでの追加方法は、byte-buddyのサイトを参照してください。

testRuntime "net.bytebuddy:byte-buddy:1.7.5"

すると、interfaceを実装していないクラスからでもMockがつくれた!

実列

以下のクラスのモックを作る。

class Calculator {

    int add(int a, int b) {
        return a + b
    }
}

テストコードは以下。

def "interfaceなしでMockをつくる" () {
    setup:
    def calc = Mock(Calculator)
    calc.add(_, _) >> 4

    expect:
    calc.add(1,2) == 4
}

これを走らせると、テストが成功する。

何が起こっているのかわからないけれども、おそらくクラスからインタフェースを自動生成しているのかな?とりあえず、便利になった。

コードは以下です。

今日はここまで。

24 Sep 2017, 08:48

Javaでsetter/getter自動生成するLombokが便利。IntelliJ+gradleでの設定

はじめに

Lombokという Javaのライブラリが便利。

アノテーションを使うことで、getter, setter, toStoring, コンストラクタなどの、

定型的なコードを自動生成することができる。

gradleでのインストール

gradleでは、build.gradleのdependenciesに以下の行を追加する。

dependencies {
    compileOnly 'org.projectlombok:lombok:1.16.18'
}

IntelliJ IDEAでの設定

まずは、プラグインを入れる。ツールバーのFile -> Preferences -> Plugins から、

Lombokを検索して、インストール。

次に、コンパイル時に、アノテーションからコードを自動生成する機能を有効にする。

Preferences – Build, Execution, Deployment – Compiler – Annotation Processors を開き

Enable annotation processing をチェック。

ここまでできたら、IntelliJを再起動。

使用例

いろいろと便利な機能があるのだが、つまみぐいして紹介。

@Getter/@Setter

このアノテーションを使うと、getter/setterを自動生成してくれる。

class Parson {
    private @Getter @Setter String name;
}
  • getName()
  • setName()

@ToString

このアノテーションを使うと、toStringを自動生成してくれる。

@ToString
class Parson {
    private @Setter String name;
}

Person(name=mikan) とか表示される。

@Data

getter/setter/コンストラクタ/toString/equals/hashCode 自動生成。すごい。

@Data
class Group {
    private String name;
}

@AllArgsConstructor

フィールドを持つ変数を引数にするコンストラクタを自動生成。

@AllArgsConstructor
class Group {
    private String name;
    private int id;
}
  • Group(String, int)

24 Sep 2017, 05:57

Spockでモックとスタブを使ってJavaコードをテストする

はじめに

前回の続きです。

前回は、基本的な文法を確認しました。今回は、モック機能を使ってみます。

テスト対象コード

想定としては、MockSampleが自分が開発しているコード。MessageManagerが他人が開発しているコードとします。

MessageManagerクラスの開発は遅延しているので、インタフェースだけ先に提供されているものとします。

こんなとき、自分が作ったMockSampleクラスをMessageManagerの依存関係をうまく扱ってテストすることを目指します。

  • MockSample.java
package sample;

public class MockSample {
    private MessageManager mgr;

    public void setManager(MessageManager mgr) {
        this.mgr = mgr;
    }

    public void sendMsg(String msg) {
        mgr.send(msg);
    }

    public int sendMsg2(String msg) {
        return mgr.send2(msg);
    }
}
  • MessageManager.java
package sample;

public interface MessageManager {
    void send(String msg);
    int send2(String msg);
}

テストコード

モックとスタブの言葉の定義は人それぞれあって混乱するのだけれども、ここでは

  • モック: 呼びだされたときに与えられるパラメータチェックと呼び出し回数をチェックするダミークラス
  • スタブ: 呼びだされた時に戻り値を返すダミークラス

とします。

モック

まずは、モックから。Mockを生成するには、

def mgr = Mock(MessageManager)

とするか、

MessageManager mgr = Mock()

で宣言します。

import sample.MessageManager
import sample.MockSample
import spock.lang.Specification

class MockSampleSpec extends Specification {

  def "呼び出し引数をチェック(Mocking)"() {
      setup:
      def sample = new MockSample()
      def mgr = Mock(MessageManager)
      sample.setManager(mgr)

      when:
      sample.sendMsg("hello")
      sample.sendMsg("hello")

      then:
      2 * mgr.send("hello")
  }
}

以下で2回呼び出しを期待して、呼び出し引数は”hello”を期待しています。

2 * mgr.send("hello")

スタブ

次にスタブです。以下の宣言で、戻り値をオブジェクトに指定します。

mgr.send2(_) >> 1
def "戻り値を返す(Stubbing)" () {
    setup:
    def sample = new MockSample()
    def mgr = Mock(MessageManager)
    mgr.send2(_) >> 1
    sample.setManager(mgr)

    expect:
    sample.sendMsg2("hello") == 1
}

例外をチェックするテストコード

戻り値の代わりに例外をスタブで発生させることもできます。

def "例外が発生したことを確認する" () {
    setup:
    def sample = new MockSample()
    def mgr = Mock(MessageManager)
    mgr.send(_) >> {throw new IllegalArgumentException()}
    sample.setManager(mgr)

    when:
    sample.sendMsg("hoge")

    then:
    thrown(IllegalArgumentException)
}

また、なにも例外が発生しなかったことを確認することもできます。

def "例外が発生しないことを確認する" () {
    setup:
    def sample = new MockSample()
    def mgr = Mock(MessageManager)
    sample.setManager(mgr)

    when:
    sample.sendMsg("hello")

    then:
    noExceptionThrown()
}

コードはgithubにもあげています。

参考

今日はここまで!

21 Sep 2017, 13:54

Spock をつかってJavaコードをテストしてみた

はじめに

仕事で spockをつかうことになりそうなので、まずはHello, World的な簡単なサンプルを実行することにしました。

環境構築

環境情報

  • JDK 1.8
  • IntelliJ IDEA 2017.2.4
  • spock 1.1(groovy2.4)

IntelliJで プロジェクト作成

IntelliJ のツールバーより、

  • [ファイル] -> [新規] を選択。
  • Gradleプロジェクトを選択。
  • Java, Groovyにチェックを入れる。

あとは、デフォルトのままにOKを押していく。

build.gradleの修正

spockをダウンロードするようにbuild.gradleを修正。

version '1.0-SNAPSHOT'

apply plugin: 'groovy'
apply plugin: 'java'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.3.11'
    testCompile "org.spockframework:spock-core:1.1-groovy-2.4"
}

code

テスト対象コード

以下のクラスをテストします。src/main/java/sample/Calculator.java

package sample

class Calculator {
    int add(int a, int b) {
        return a + b
    }
}

テストクラス

以下のテストクラスを用意します。 test/groovy/CalculatorSpec

package spock

import sample.Calculator
import spock.lang.Specification

class CalculatorSpec extends Specification {

    def '足し算1'() {
        setup:
        Calculator calc = new Calculator()

        expect:
        calc.add(1,1) == 2
    }
}
  • setup: ・・・ 前処理でやりたいことを書く
  • expect: ・・・期待するテスト結果を書く == でAssertする。

CalculatorSpecを選択して、実行する。テストが成功します。

テストクラスその2

こんどはわざと失敗するテストを書いてみます。

def '足し算1'() {
    setup:
    Calculator calc = new Calculator()

    expect:
    calc.add(1,2) == 4
}

以下のようにわかりやすいエラー表示が出力されます。

Condition not satisfied:

calc.add(1,2) == 4
|    |        |
|    3        false
sample.Calculator@5e316c74

予想 :4

実際   :3

リファクタリング

前処理、後処理をまとめる関数が用意されています。

  • 前処理: setup(){}
  • 後処理: cleanup(){}
package spock

import sample.Calculator
import spock.lang.Specification

class CalculatorSpec extends Specification {
    def calc

    def setup() {
        calc = new Calculator()
    }

    def cleanup() {
        calc = null
    }

    def '足し算1'() {
        expect:
        calc.add(1,1) == 2
    }

    def '足し算2'() {
        expect:
        calc.add(1,2) == 3
    }
}

データ駆動テスト

where:を使うことで、データテーブルを使ったテストがかけます。

def "足し算:データ駆動テスト"() {
    expect:
    calc.add(a, b) == c

    where:
    a|b|c
    1|1|2
    2|3|5
    3|4|7
}

その他

コードはgithubにもあげています。

続き

今日はここまで!

18 Sep 2017, 02:24

Java本格入門(モダンスタイルによる基礎からオブジェクト指向・実用ライブラリまで)を読んだ

仕事でJavaをつかうことになった。

2年間Javaを使ってなかったので、忘れてしまった。

さびついた頭脳に磨きをかけるために、Java本格入門を読みました。アクロ本というらしい。

特徴

Javaの文法がコンパクトにまとまっている

Javaの文法を外観できます。

Java8まで対応しているので、ラムダ式やストリームまでカバーしています。

また、各Javaのバージョンごとの説明が詳しいです。

裏タイトル: 35歳からのJava入門

この本の裏タイトルは、35歳からのJava入門です。

世の中には、プログラマ35歳定年説というものがありますが、私はこれは嘘だと思います。

その根拠は、新人研修で習った知識で10年開発をやってきて、

時代の流れについていけなくなった人を指すのだと思います。

学び続ける人に定年はないです。そして、この本は、Java5,6を勉強して、

そのまま月日が経ってしまった、人に対する視点で書かれています。

新しくなったJava7,8の仕様がふんだんに紹介されています。

Javaの開発について書かれている

Javaの文法のみに終始している本とは違い、この本には、開発でつかうための知識が書かれています。

具体的には、(ビルド、javadoc, JUnit, FindBugs, Jenkins, JSON, Log…)

などの知識がてにはいります。実践的なJavaの入門書です。

ミッションクリティカルな配慮

秀逸なのは、例外についての説明や、並列処理についての説明です。

ここは、ミッションクリティカルなプロジェクトの経験を元に、とても丁寧に解説されています。

どこでどんな例外を捕捉するべきか、どんなライブラリを使うとスレッドせーフなプログラムになるか、

読んでいて知らないことが多いこのあたりの領域は目にウロコです。

感想

Java7,8の知識が知りたかったので、この本は当たり。

Java7,8ではこういう書き方があるということが書かれている。

自分は、一応 Java8 SE Silverの資格と2年間のJavaの開発経験があるので、

初心者ではない。そんな自分にとっては優しすぎる本は物足りない。

この本は、初心者以上の中級者をターゲットにして書かれているので、

自分にはちょうどよい難易度だった。

また、Java8で追加されたラムダ式やストリームについてもよくまとまっているので、

何度も参照して使いこなしたいところだ。

デザインパターンとか載っているけれども、これはおまけかな。

本格的に学ぼうとするなら、他の書籍をあたった方がいい。

とにかく、Javaに関する知識が盛りだくさんなので、

机の脇においておいて、困ったらいつでも参照できるようにしたい。

16 Sep 2017, 10:54

Java 8 で Command Pattern

はじめに

3年前の記事の補足です。

Java8で追加されたラムダ式でコマンドパターンを書き換えてみた。

かつては、Java 6を使っていたので、匿名クラスを利用して処理を実行と分離していた。

ポイント

  • インターフェースに 明示的に @FunctionalInterface アノテーションを追加する。
  • ラムダ式を使って、無名関数を渡す。

コード