2011年12月31日土曜日

Confluenceをインストールしてみた。

昨日のJIRAのインストールに続き、Confluenceをインストールして見ました。

インストール手順は次のページを参照してください。(手抜き)

Confluence初心者がConfluenceインストールに挑戦してみた

インストール作業及びJIRAとの連携は思ったよりも難しくないですよ。

では、みなさん2011年も残りあと30分くらい。良いお年をおすごしください。

2011年12月30日金曜日

あんまり書きたくない投稿

まあ、このブログを定期的に購読されている方
(そんなのいるかどうか知らんが…)
は、ご存知のことかと思いますが、

私、今月半ばまでソーシャルゲームの開発でベトナムでデスマっていました。
結局、このプロジェクトは発注者側が契約解除という形でプロジェクトが収束したのですが…

その時に感じた今のソーシャルゲーム開発の落とし所と言うか、SIに慣れきったオレには開発プロセスとしてあかんやろコレと思ったところを列挙していきます。
もちろん、私自身のマネージメント能力が足りない部分もあるので、そのあたりは良い感じに読んでください。
また、私は開発者であるので、あくまで開発者側の視点であるということも考慮の上、読んでください。

  • 発注者(ゲーム企画)側はたいてい要求と仕様の違いを全く理解していない。
  • 最初に仕様書とか要望書とか渡されるが、そんなものは当てにはならない。かならず、プロジェクトの終盤で大きな変更が入る。
  • 新規にソーシャルゲームに参加しても勝てない。
  • 発注者(ゲーム企画者)側があとは実装するだけと言っても、それを信用してはならない。
  • 発注者(ゲーム企画者)側が仕様が確定していると言っている場合は、たいてい開発可能性を考慮していない上での仕様確定なので、その分の仕様確定プロセスは見積もりに載せておいたほうがよい
  • コピーゲームっぽい要素があって、そのゲームをやったことがないなら、そのゲームを勉強する期間を1ゲームに付き1人月程度は工数に上乗せしたほうがよい
    • 発注者(ゲーム企画者)側が該当するゲームに関連するゲームの開発を始めてやる場合には、仕変があとから大量変更するので、参考ゲームの数分、人月を上乗せしたほうがよい
  • 仕様変更が発生した場合は、リリース時期等の調整を行いましょうと確約しても、そんなものは守られない
  • マルチプラットフォームでやりたいということはしばしばあるが、自社でマルチプラットフォームの経験がない、あるいは発注者(ゲーム企画者)側がその経験がない場合は、対応しないほうが良い。それがどんな理由を並べられたとしても。もし、それでもとゴリ押しされるなら工数は2倍とっとけ。
  • 発注者(ゲーム企画者)側で画面を用意しますとかいうことを言われるが、この画面の精度は当てにしてはならない。はっきり言って同じ画面を作りなおすだけの工数は上乗せしておけ。
  • 運用を考慮して設計して欲しいと言われたら、実開発コストに倍位の工数を加算しておいたほうがよい。


いろいろと不満だったことを書き連ねました。

少しづつ解説。

No.1とNo.4とNo.5に関して

まあ、得てして発注者というのは要求と仕様との区別はわからないものです。
これは仕方ありません。
だから、こういう部分についてはプロフェッショナルである開発者側がちゃんと要求と仕様とを整理する必要があります。

…で、ここからが重要ですが、

「あとは実装するだけ」と発注者側が言ったとしても、要求から仕様を抽出、もとい、ちゃんと要求を整理する必要はあります。
発注者のもう要求も整理できているし、仕様も固まっているなんていう甘い文句につられてはいけません。

仕様が整理できているというのは、ゲームとしての企画ができているという状態で、ゲームを実装する仕様としての仕様はまったく定義されていません。
なので、開発側としてはこの部分の工数をけちってはいけません。
そして、これは発注者と開発者とで同じ言葉を使いながら、まったく別の意味を指しているので、互いに共通認識をあわせておく必要がある場所です。

No.2について

はい、これもよくあります。SIerでもよくありますね。
なんかプロジェクト終盤になって、考慮漏れが発覚するケース。

これ、まだエンプラ方面だったらまだ意外となんとかなるケースが多いのです。
代替策(これによって、目的をどのような形でもいいから達成できる方法)を何か一つ考えればいいから。

ゲームではこの辺は違います。
目的を達成するのは当然のことで、その上でクオリティを重視してきます。
なので、考慮漏れだった箇所について確認すると、もれなくその考慮漏れを隠すかのようにメチャメチャでかい仕様が追加されて返ってきます。

ちなみに、ベトナムでデスマっていたときに、このような確認をしましたが、半端無い仕様追加で返ってきました。

むしろ、この返答によって、私は発注者(ゲーム企画者)側の人間が、日本語を理解していないのではないか、ゲーム開発を終わらせる気がないのではないかという疑念を抱きました。

No.6に関して


発注者(ゲーム企画者)側が、「これこれの部分はドラ○コレやればすぐわかります」とか言った場合は、ゲーム企画者側の協力放棄にも近い(ゲームで何をやりたいのか要求を明確に伝えようとする努力を怠っている)ので、こんなやり取りがあるのであれば、ちゃんと参考ゲームを理解するための工数およびゲームの課金にかかる費用は請求しておいた方が良いでしょう。(あくまでSIerの立場でしゃべっていますが…)。

というのも、こういう説明でかたされる場合は、ゲームの要求あるいは仕様に明文化された形で仕様が残らないので、開発側に非常にリスクが残ります。
なので、こういう説明のされ方をなされた場合は、参考ゲームを実施した記録を保存しておき、どのゲームのどの箇所であるかリバースエンジニアリングして明文化しておいたほうが無難です。

で、で、で、さらにゲーム開発を現在するということは課金要素が必ず関係していますので、参考ゲームの調査には課金部分を通過しないといけません。
それは費用ですので、ちゃんと請求してください。

さらに、No.2でも触れましたが、複数ゲームのコピペな場合は、複数ゲームの要素で埋め尽くされていて、その部分を解体する必要があります。キメラを解体して、各内臓諸器官をどの動物のどの臓器であるかをマッピングする作業が発生してきます。なので、プロジェクト終盤の仕変に備えて、工数は多めに見積もっておいたほうが良いでしょう。

No.7くらい


諦めましょう。
大概はスコープは変えてくれません。

ちゃんとスケジュールを出しても、どうにかして間に合わせろと言われます。
そういう場合は、発注者(ゲーム企画者)側のキーマンを拉致って、開発現場の中に投入して、軟禁状態にしてください(キケン)。
そうでもしない限り、無理なんだということを実感させることは不可能です。

No.8くらい


マルチプラットフォームは今のうちに抑えておきましょう。
Unity、Titanium、Phonegapなどがありますが、Unityは知らんですが、他のはWebViewを用います。
HTML5の企画などに精通しておきましょう。

それでも、Native周りの実装、特にIn-App PurchaseとかIn-App Billing、C2DM、APNsとかは必ず関わってきますので、Android、iOSともに勉強していないと厳しいです。
コレに加えて、ゲーム関連のプラットフォームなどがありますので、このあたりはちゃんと勉強しておかないと厳しいです。

で、開発者側がそれらのスキルがなく、発注者側にもそれらのスキルがない場合は、地獄を見ますので、それでもやりたいと発注者側が主張するときは、片方だけに限定してもらえるように交渉するしかありません。

または、もう片方のプラットフォームには倍の工数をかけて見積もりを提出してください。

No.9


これは、私のプロジェクトだけがおかしかったのかもしれませんが、画面制作担当者がゲームの仕様に詳しくなく、出来が悪い画面ばかり送りつけられて、心のなかで「んだよ、これ!」と思いながら、ここのこの部分、こういう動きをした場合の画面がないので製作していただけませんか?とお願いする次第になります。

私はデザイナーさんを馬鹿にする気持ちは全くありませんが、チームとして目的が共有できていないような発注者(ゲーム企画者)のもとで作業しているデザイナーさんはヤル気が欠如してしまいがちです。そしてそれらは、開発者側の作業遅延として発注者側から指摘されることが多いです。

ですので、発注者側が画面を準備しますという場合は、ほぼ作りなおしを想定して工数をくんでおいたほうが安全です。



結論


この世からコピゲーはなくなるべき。




2011年12月28日水曜日

今読んでいる技術書

最近、困ったことに朝低体温症(34℃くらいしかない)で体が動かせず困っているmikeです。

いろふさんの記事に触発されて、とりあえず、今読んでおきたい本をピックアップして見ました。

Mike Cohn著、安井力、角谷信太郎訳、『アジャイルな見積もりと計画づくり 』(毎日コミュニケーションズ)



まあ、いまさらですが、ベトナム出張したプロジェクト明らかに納期に対して要求機能が多すぎて、しかも要求がまとめられていないという状況でデスマりました。その挙句(…)な状況で、プロジェクトの運営とか顧客に価値を提供するには顧客にとって何が一番の価値で、それを早い段階で提供できることが今のプロジェクト運営には求められていると思っています。だからといって、アジャイルがそれに応えられるかどうかはわかりません。ただし、顧客の期待をコントロールすること、こういうアジャイル特有のプロセスは今確実にみにつけて置かなければ、プログラマーとして失格だと思っています。というわけで、この本を先ずは取り上げました。

Amazonで3,360円で売っているようです。



Christian Johansen著、『Test-Driven JavaScript Development』(Addison-Wesley Professional, 2010/9)





邦訳も売り出されています。が、残念ながら、私が日本に帰ってきたときは、すでに手に入らない状況だったので、まあ英語でいいやと思って購入。なんかこの一冊でJavascriptパターン本に匹敵するJavascript力を手に入れられそうな気がしなくもない。ちなみにJavascriptパターン本も良書なので、ぜひ読んでおきたい本です。

Amazonで3,652円くらいです。






山本さん、上原さん、杉浦さん著『Grails 徹底入門』(翔泳社、2008年)



最近、Grails2.0がリリースされました。チュートリアルを見ていても、簡単なものしか作れなさそうなので、バージョンは古いですが、1.0からコツコツ勉強していくのが最短の道かと思っています。
なぜ、Grailsに注目しているかというと、ある程度規約を覚えてしまえば、Webアプリケーションがすぐ作れて、そのぶん顧客からのフィードバックを早い段階から得られるあたりが非常に気になっています。
言い換えると、Ruby on Railsに非常に注目しているのですが、Java屋からRubyに乗り換えるの結構厳しくて、Groovyの方が自分には近かったという怠惰な理由もあるんですけどね。

Amazonで3,780円くらいです。


おっと、Grailsをやる前に抑えておきたいのが…

阪田浩一著『SpringによるWebアプリケーションスーパーサンプル』(ソフトバンククリエイティブ、2010年)

それとあわせて

Maria Odea Ching著『Apache Maven2 Effective Implementaion』(Packt Publishing 2009)




Spring関連の書籍、日本では意外と少なくて、勉強したくて購入したのですが、全然進んでいません。
ただ、考え方などはだいたいわかっているので、ついでということで、Mavenスタイルでの開発、テストを実施しています。

Mavenに関してはかなり音痴なので、この本を読みながら、IntelliJ IDEAにほとんどお任せという感じになっていますが、これでも結構勉強になっています。
早い段階で、Springの基本的なところは抑えて、Spring Rooに進みたいというのが今の私の心情。

Springの本は、3,990円。

Maven2の方は3,245円くらいで売っています。





ちょっとプラスアルファ的なもの

Apache Maven3 Cookbook



意外と色々な使い方が載っている良書です。

Androidプロジェクトをテストプロジェクトを含めてMaven管理下に置く方法などのちょっとしたTipsが載っています。

ちなみにSpringに関しても結構大きな部分を割いており、さっきのSpring関連でもお世話になっています。

なお、3,279円くらいです。








ちなみに、今mongodbのこと調べていますが、参考文献はなしです。
ネットに転がっている情報を漁っています。





2011年12月24日土曜日

hamcrestを拡張してmoreThanとか作ってみた

JavaAdventCalendarの24日目のエントリーです。

最初はenumに関してエントリーを書こうとしていたのですが、そのネタについてコードを書いている途中で、どうしても気持ち悪いテストコードを書く羽目になったので、なんかいい表現ないかなと考えているうちに、hamcrestの拡張を書いてしまいました。

というわけで、タイトル

hamcrestを拡張してmoreThanとか作ってみた


です。

そもそものそもそも


もともと書いていた気持ち悪いテストコード…

@Test
    public void testCompare() {
        Trump queen = Trump.valueOf("Queen");
        Trump king = Trump.valueOf("King");
        assertThat(queen.compareTo(king) < 0, is(true));
    }

    private enum Trump implements Comparable<Trump> {
        King {
            int value = 13;
            @Override
            public int value() {
                return value;
            }
            @Override
            public int compareTo(Trump trump) {
                return this.value() - trump.value();
            }
        }, Queen {
            int value = 12;
            @Override
            public int value() {
                return value;
            }
            @Override
            public int compareTo(Trump trump) {
                return this.value() - trump.value();
            }
        }
        abstract public int value();
        abstract public int compareTo(Trump trump);
    }


assertThatの中が気持ち悪い…

まあ、別にboolean result = queen.compareTo(king) < 0を変数として取り出せばよいだけの話といえば、それまでなのですが、やっぱりなんか気持ち悪い。

で、junit more thanググってみたけど、こんな感じでした(´・ω・`)

というわけで、moreThanみたいなことをやりたかったので、自前で作って見ることにしました。

拡張してみよう!


で、コードはこんな感じ。

org.hamcrest.core.MoreThan.java

package org.hamcrest.core;

import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.hamcrest.Matcher;

public class MoreThan<T extends Comparable<T>> extends BaseMatcher<T> {

    private final T matcher;
    private final Class<T> klass;

    public MoreThan(T matcher) {
        this.matcher = matcher;
        this.klass = (Class<T>) matcher.getClass();
    }

    @Override
    public boolean matches(Object o) {
        if(o.getClass() == klass) {
            T object = klass.cast(o);
            int result = matcher.compareTo(object);
            return result < 0;
        } else {
            return false;
        }
    }

    @Override
    public void describeTo(Description description) {
        description.appendText("more than ").appendValue(matcher);
    }

    @Factory
    public static <T extends Comparable<T>> Matcher<T> moreThan(T value) {
        return new MoreThan<T>(value);
    }
}


拡張して自分好みのMatcherを作るにはorg.hamcrest.BaseMatcher<T>を継承するようです。

メソッドmatchesは実際の値の比較を記述します。
メソッドdescribeToはテストが失敗したときに表示される文章を記述します。

使い方


使い方は至って簡単です。

MoreThanTest.java

@Test
    public void testCalendarCase() {
        Calendar cal1 = Calendar.getInstance(
            TimeZone.getTimeZone("Asia/Tokyo"));
        cal1.set(2011, 12, 24, 15, 54, 55);
        Calendar cal2 = Calendar.getInstance(
            TimeZone.getTimeZone("Asia/Tokyo"));
        cal2.set(2011, 12, 24, 15, 54, 56);
        assertThat(cal2, moreThan(cal1));
    }


ちなみに、残念な所があります。
  • intlongなど、異なる型の値を比較できない。

まあ、そのあたりは型安全様にあわせてあげて下さい。

その他、LessThanLessThanEqualMoreThanEqualなども作ってみましたので、まあ、興味があったら見てツッコミを下さい。

明日のJava Adventカレンダーは、daisuke-mさんです。


余談


このエントリーを掲載した後、こんなツイートが寄せられました。




うぉ、本当だ…!


さらには、



なんと!。
JUnit4にバンドルされているhamcrestは古いバージョンだと!

そして、極めつけは、


というわけで、明日のdaisuke_mさんのページを見ると…

都元ダイスケ IT-PRESS : [Java][test]hamcrestのMatcherメモ

バッチリまとめられておる。


というわけで、車輪の再発明をしてしまったようだ…。


そこで、ソースを読んでみる


まあ、情弱なのは仕方ないので、もうちょい突っ込んでみてみる。


hamcrest-libraryのソースはcode.google.comにあるようです。

org.hamcrest.Matchers.javaというソースはなかったのですが、その本体となるorg.hamcrest.number.OrderingComparisonというクラスがあったので、それを読んでみました。


/*  Copyright (c) 2000-2009 hamcrest.org
 */
package org.hamcrest.number;

import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;

public class OrderingComparison> extends TypeSafeMatcher {
    private static final int LESS_THAN = -1;
    private static final int GREATER_THAN = 1;
    private static final int EQUAL = 0;
    private final T expected;
    private final int minCompare, maxCompare;

    private static final String[] comparisonDescriptions = {
            "less than",
            "equal to",
            "greater than"
    };

    private OrderingComparison(T expected, int minCompare, int maxCompare) {
        this.expected = expected;
        this.minCompare = minCompare;
        this.maxCompare = maxCompare;
    }

    @Override
    public boolean matchesSafely(T actual) {
        int compare = Integer.signum(actual.compareTo(expected));
        return minCompare <= compare && compare <= maxCompare;
    }

    @Override
    public void describeMismatchSafely(T actual, Description mismatchDescription) {
        mismatchDescription.appendValue(actual).appendText(" was ")
                .appendText(asText(actual.compareTo(expected)))
                .appendText(" ").appendValue(expected);
    }

    public void describeTo(Description description) {
        description.appendText("a value ").appendText(asText(minCompare));
        if (minCompare != maxCompare) {
            description.appendText(" or ").appendText(asText(maxCompare));
        }
        description.appendText(" ").appendValue(expected);
    }

    private String asText(int comparison) {
        return comparisonDescriptions[comparison + 1];
    }

    /**
     * @return Is value = expected?
     */
    @Factory
    public static > Matcher comparesEqualTo(T value) {
        return new OrderingComparison(value, EQUAL, EQUAL);
    }

    /**
     * Is value > expected?
     */
    @Factory
    public static > Matcher greaterThan(T value) {
        return new OrderingComparison(value, GREATER_THAN, GREATER_THAN);
    }

    /**
     * Is value >= expected?
     */
    @Factory
    public static > Matcher greaterThanOrEqualTo(T value) {
        return new OrderingComparison(value, EQUAL, GREATER_THAN);
    }

    /**
     * Is value < expected?
     */
    @Factory
    public static > Matcher lessThan(T value) {
        return new OrderingComparison(value, LESS_THAN, LESS_THAN);
    }

    /**
     * Is value <= expected?
     */
    @Factory
    public static > Matcher lessThanOrEqualTo(T value) {
        return new OrderingComparison(value, LESS_THAN, EQUAL);
    }
}



@Factoryのあたりを眺めてから、describeToなどを眺めると、思わずニヤリとしてしまいますね。
やっぱり本家本元のソースには勝てんかったか…。


で、なんで、Matchersがないのか気になったのですが、ひょっとしてhamcrest-generatorあたりで自動で処理しているのかな~などと思ってみたりしましたが、まだそこまでちゃんとソースを読んでおりません…

ん~、まあ、でも少し勉強になりました。

ご指摘をくださった皆様、大変有難う御座います。




2011年12月21日水曜日

恥ずかしくて今さら聞けない、GMongo講座 2

前回の投稿からずいぶんと時間が経ってしまいました。

いろいろとすったもんだがあったので、まあ、ちょっとアレして下さい。

で、気づいたらG* Advent Calendarの担当日になっていて、

本当はGrails2.0リリース記念で記事を一本書きたかったのですが、

若干、準備不足ということで、

今回の『恥ずかしくて今さら聞けない、GMongo講座 2』でもって、G* Advent Calendarの記事とさせて下さい。

で、これまでの『恥ずかしくて今さら聞けない、GMongo講座』ですが、以下のエントリーがあります。


これらを参考にしてください。

では、本日の内容ですが、


と同等の内容をGroovyでやります。

まあ、つまりクエリーですね。

データの準備


今日はクエリですので、データをまんべんなく準備します。ってどこかで書いた記憶ががが…

例によって、JUnit@Beforeにて準備します。

MongoTest.groovy

import com.gmongo.GMongo
import org.junit.Before
import com.mongodb.DB
import org.junit.After


    GMongo mongo

    DB db

    static List<Integer> list = [1,2,3,4,5,6,7,8,9]

    static String name = 'name'

    @Before
    public void setUp() {
        mongo = new GMongo("localhost", 27017)
        db = mongo.getDB("mydb")
        def store = db[items]
        def objects = []
        list.each { x ->
            def base = x
            list.each { y ->
                base += y
                def multiple = x * y
                objects << [
                        name : name,
                        xIndex : x,
                        yIndex : y,
                        sum : base,
                        multiple : multiple]
            }
        }
        objects.each { obj ->
            store.insert(obj)
        }
    }




これで、81個のデータを準備します。

念のため、テストデータが揃っているかテストしておきましょう。

MongoTest.groovy

import org.junit.Test
import com.gmongo.GMongo
import org.junit.Before
import com.mongodb.DB
import org.junit.After


    GMongo mongo

    DB db

    static List<Integer> list = [1,2,3,4,5,6,7,8,9]

    static String name = 'name'

    int count = 81

    @Before
    public void setUp() {
        mongo = new GMongo("localhost", 27017)
        db = mongo.getDB("mydb")
        def store = db[items]
        def objects = []
        list.each { x ->
            def base = x
            list.each { y ->
                base += y
                def multiple = x * y
                objects << [
                        name : name,
                        xIndex : x,
                        yIndex : y,
                        sum : base,
                        multiple : multiple]
            }
        }
        objects.each { obj ->
            store.insert(obj)
        }
    }

    @After
    public void tearDown() {
        db.items.remove([:])
    }

    @Test
    public void testSetUp() {
        def list = db.items.find()
        assert  list.size() == count
    }



で、テストを実行します。


はい、大丈夫そうです。

一応、念のため、登録したデータはこんな感じになっています。

1 2 3 4 5 6 7 8 9
1 sum : 2
multi : 1
sum : 4
multi : 2
sum : 7
multi : 3
sum : 11
multi : 4
sum : 16
multi : 5
sum : 22
multi : 6
sum : 29
multi : 7
sum : 37
multi : 8
sum : 46
multi : 9
2 sum : 3
multi : 2
sum : 5
multi : 4
sum : 8
multi : 6
sum : 12
multi : 8
sum : 17
multi : 10
sum : 23
multi : 12
sum : 30
multi : 14
sum : 38
multi : 16
sum : 47
multi : 18
3 sum : 4
multi : 3
sum : 6
multi : 6
sum : 9
multi : 9
sum : 13
multi : 12
sum : 18
multi : 15
sum : 24
multi : 18
sum : 31
multi : 21
sum : 39
multi : 24
sum : 48
multi : 27
4 sum : 5
multi : 4
sum : 7
multi : 8
sum : 10
multi : 12
sum : 14
multi : 16
sum : 19
multi : 20
sum : 25
multi : 24
sum : 32
multi : 28
sum : 40
multi : 32
sum : 49
multi : 36
5 sum : 6
multi : 5
sum : 8
multi : 10
sum : 11
multi : 15
sum : 15
multi : 20
sum : 20
multi : 25
sum : 26
multi : 30
sum : 33
multi : 35
sum : 41
multi : 40
sum : 50
multi : 45
6 sum : 7
multi : 6
sum : 9
multi : 12
sum : 12
multi : 18
sum : 16
multi : 24
sum : 21
multi : 30
sum : 27
multi : 36
sum : 34
multi : 42
sum : 42
multi : 48
sum : 51
multi : 54
7 sum : 8
multi : 7
sum : 10
multi : 14
sum : 13
multi : 21
sum : 17
multi : 28
sum : 22
multi : 35
sum : 28
multi : 42
sum : 35
multi : 49
sum : 43
multi : 56
sum : 52
multi : 63
8 sum : 9
multi : 8
sum : 11
multi : 16
sum : 14
multi : 24
sum : 18
multi : 32
sum : 23
multi : 40
sum : 29
multi : 48
sum : 36
multi : 56
sum : 44
multi : 64
sum : 53
multi : 72
9 sum : 10
multi : 9
sum : 12
multi : 18
sum : 15
multi : 27
sum : 19
multi : 36
sum : 24
multi : 45
sum : 30
multi : 54
sum : 37
multi : 63
sum : 45
multi : 72
sum : 54
multi : 81

等価


さて、これからはクエリの練習をやってみます。

multipleの値が24になるものを探すと、
  • 3 x 8 = 24
  • 4 x 6 = 24
  • 6 x 4 = 24
  • 8 x 3 = 24

の4つが該当します。

というわけで、イコールクエリーを発行するわけですが、

これの指定方法は簡単です。

findメソッドの引数にハッシュを渡すだけです。

上記の例では

db.items.find([multiple : 24])

という形で検索することができます。

では、テストをしてみましょう。

MongoTest.groovy

import org.junit.Test
import com.gmongo.GMongo
import org.junit.Before
import com.mongodb.DB
import org.junit.After


    GMongo mongo

    DB db

    static List<Integer> list = [1,2,3,4,5,6,7,8,9]

    static String name = 'name'

    int count = 81

    @Before
    public void setUp() {
        mongo = new GMongo("localhost", 27017)
        db = mongo.getDB("mydb")
        def store = db[items]
        def objects = []
        list.each { x ->
            def base = x
            list.each { y ->
                base += y
                def multiple = x * y
                objects << [
                        name : name,
                        xIndex : x,
                        yIndex : y,
                        sum : base,
                        multiple : multiple]
            }
        }
        objects.each { obj ->
            store.insert(obj)
        }
    }

    @After
    public void tearDown() {
        db.items.remove([:])
    }

    @Test
    public void equalQuery() {
        def result = db.items.find([multiple : 24])
        assert result.size() == 4
        result.each {
            println it
        }
    }



実行結果は次のとおりです。


みごとに予定通り検索できています。

不等号


不等号も、前回と同様に$gte;のような記号をハッシュのキーとして用いることで指定できます。

例えば、xIndexの値が3より小さい場合は$lt;を用いて、

[xIndex : [$lt : 3]]
となります。

では、実際にテストを実施してみます。

このテストでは、xIndexの値が3より小さく、1以上の値を検索しています。

MongoTest.groovy

import org.junit.Test
import com.gmongo.GMongo
import org.junit.Before
import com.mongodb.DB
import org.junit.After

    GMongo mongo

    DB db

    static List<Integer> list = [1,2,3,4,5,6,7,8,9]

    static String name = 'name'

    static String items = 'items'

    int count = 81

    @Before
    public void setUp() {
        mongo = new GMongo("localhost", 27017)
        db = mongo.getDB("mydb")
        def store = db[items]
        def objects = []
        list.each { x ->
            def base = x
            list.each { y ->
                base += y
                def multiple = x * y
                objects << [
                        name : name,
                        xIndex : x,
                        yIndex : y,
                        sum : base,
                        multiple : multiple]
            }
        }
        objects.each { obj ->
            store.insert(obj)
        }
    }

    @After
    public void tearDown() {
        db.items.remove([:])
    }

    @Test
    public void testQuery() {
        def store = db['items']
        def list = store.find([xIndex : [$lt : 3, $gte : 1]])
        assert list.size() == 18
        list.each {
            assert it.xIndex in new IntRange(1,2)
        }
    }



そして、テスト結果は次のような感じです。


GMongoを使うと、かなりmongoを直接扱っている感じがして、非常に融合性が高い気がします。

では、次回はインデックスについて学びたいと思います。

それと、次回のG* Advent Calendar『クソゲーによる自己紹介』で見事Groovy合宿のGDK48で最優秀賞を獲得した@uehajさんです。



2011年12月15日木曜日

恥ずかしくて今さら聞けない、GMongo講座 2-

今日は、恥ずかしくて今さら聞けない、GMongo講座の第二回目を実施しようと思ったのですが、

ちょっと趣旨を変えています。

前々回述べた(かなぁ…)ように、GMongoはmongo-java-driverのthinラッパーで、Javaとの結びつきが容易なわけです。

これはJavaとの親和性を保つというGroovy特有の思想になります。

そこで、GMongoを使いつつ、前回程度のレベルをJavaでも実装してみるというのが今日の目的になります。

題して、

恥ずかしくて今さら聞けない、GMongo講座 2- GMongoをつかってmongo-java-driverを勉強してみる


です。

dependency


前回ではライブラリーのダウンロードにはzipを落とせ的なことを書いたと思います。

で、実際にやってみた方はご存知のことだと思いますが、GMongo自体はmongo-java-driverのthinラッパーなので、mongo-java-driverが依存ライブラリーになっています。

その点で、Mavenを使った人や、@GrabGradleを使った人の方が楽だったかもしれません。

私も依存性の解決にはMavenを用いており、下記のコードによって、mongo-java-driverが入手できています。


<dependencies>
        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy</artifactId>
            <version>1.8.4</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.gmongo</groupId>
            <artifactId>gmongo</artifactId>
            <version>0.9.2</version>
        </dependency>
    </dependencies>




こういう依存性の解決などには、Mavenすごい役立ちますね。

う〜ん、ゆとりとか関係なく、勉強したほうがよさそうですね…

では、mongo-java-driverを使ってmongodbを触っていきたいと思います。


クライアント起動インスタンス化


ここには、GMongoを使っちゃいます。

基本的には便利なものは便利なものを利用するというスタンスを取ります。

MongoTestJava.groovy

import com.gmongo.GMongo
import org.junit.Before

    GMongo mongo;

    @Before
    public void setUp() {
        mongo = new GMongo('localhost', 27017);
    }



DBの選択とスキーマ取得


あっ、これも全然変わりませんw

MongoTestJava.groovy

import com.gmongo.GMongo;
import com.mongodb.DB;
import com.mongodb.DBCollection;

import org.junit.Before

    GMongo mongo;

    DB db;

    DBCollection items;

    @Before
    public void setUp() {
        mongo = new GMongo("localhost", 27017);
        db = mongo.getDB("mydb");
        items = db.getCollection("items");
    }


ちなみに、
  • com.mongodb.DB
  • com.mongodb.DBCollection
というクラスはmongo-java-driverライブラリーに含まれるクラスです。


レコードの作成


ここはJavaだと手順が増えます。

そこは型安全を是とするJavaだと仕方がない場面です。

MongoTestJava.groovy

import com.gmongo.GMongo;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.BasicDBObject;

import org.junit.Before

    GMongo mongo;

    DB db;

    DBCollection items;

    @Before
    public void setUp() {
        mongo = new GMongo("localhost", 27017);
        db = mongo.getDB("mydb");
        items = db.getCollection("items");
        List<DBObject> list = createDbList();
    }

    private List<DBObject> createDbList() {
        List<DBObject> list = new ArrayList<DBObject>();
        for(int i = 0; i < 10; i++) {
            BasicDBObject dbObject = new BasicDBObject();
            dbObject.put("name", "data");
            dbObject.put("value", 1);
            list.add(dbObject);
        }
        return list;
    }


このcom.mongodb.BasicDBObjectというクラスはjava.util.LinkedHashMap<String, Object>を継承しているクラスで、これがJSONデータを表します。

com.mongodb.BasicDBObject#toString()というメソッドでは、JSON形式に変換された文字列が返されます。

一度試してみましょう。


@Test
    public void testJson() {
        List<Integer> list = new ArrayList<Integer>();
        for(int i = 0; i < 10; i++) {
            list.add(i);
        }
        DBObject object = new BasicDBObject();
        object.put("data", "value");

        DBObject dbObject = new BasicDBObject();
        dbObject.put("numero", 1);
        dbObject.put("bool", false);
        dbObject.put("nil", null);
        dbObject.put("key", "KeyString");
        dbObject.put("ArrayData", list);
        dbObject.put("objectData", object);

        System.out.println(dbObject.toString());
        assertThat(dbObject.toString().contains("null"), is(true));
        assertThat(dbObject.toString().contains(" : {"), is(true));
        assertThat(dbObject.toString().contains(" : ["), is(true));
    }


結果はこんな感じです。


JSONになっていますね。
また、JSON内にJSONオブジェクトも生成できています。


レコードの登録


残すところはinsertです。

これは次になります。

MongoTestJava.groovy

import com.gmongo.GMongo;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.BasicDBObject;

import org.junit.Before


    GMongo mongo;

    DB db;

    DBCollection items;

    @Before
    public void setUp() {
        mongo = new GMongo("localhost", 27017);
        db = mongo.getDB("mydb");
        items = db.getCollection("items");
        List<DBObject> list = createDbList();
        items.insert(list);
    }

    @Test
    public void testSetUp() {
        assertThat(items.find().size(), is(10));
    }


では、実際にテストしてみましょう。



見事通りました。

MongodbはJSONデータを扱うので、型の部分でやや面倒な所がありますが、Javaでも行けますね。

というわけで、次回はGMongoの本編です。



2011年12月14日水曜日

恥ずかしくて今さら聞けない、mongodb講座 2

はい、始まりました。

今更感があるものをあえて後ろから追いかけていくこの企画。

世の中のスピードに負けずに、今日もテキトー感満載でのんびりやりますよ。

今日の目的はクエリーです。

データ準備


今日はクエリーですので、データをまんべんなく準備します。

データの挿入は前回にやったとおり、

db.collection.insert({jsondata})です。


> var ary = [1, 3, 7];
> var bry = [5, 11, 2];
> var cry = ["hop", "step", "jump"];
> cry.forEach(function(c) {
......   ary.forEach(function(a) {
......     bry.forEach(function(b) {
......       db.list.insert(
......         {
......           "type" : c,
......           "aIndex" : a,
......           "bIndex" : b,
......           "sum" : a + b,
......           "multi" : a * b
......         }
......       );
......     })
......   })
...... });
> assert(db.list.find().length() === 27, "check size");
> 


はい、 3 x 3 x 3 = 27 のデータが入っているようですね。

クエリ


じゃあ、一応リストを出力してみましょう。
なお、これはSQLで言うところの、

SELECT
    *
FROM
    LIST

にあたります。

これは、db.list.find()で出力できます。


> db.list.find()
{ "_id" : ObjectId("4ee7bd7b4238f3326533177f"), "type" : "hop", "aIndex" : 1, "bIndex" : 5, "sum" : 6, "multi" : 5 }
{ "_id" : ObjectId("4ee7bd7b4238f33265331780"), "type" : "hop", "aIndex" : 1, "bIndex" : 11, "sum" : 12, "multi" : 11 }
{ "_id" : ObjectId("4ee7bd7b4238f33265331781"), "type" : "hop", "aIndex" : 1, "bIndex" : 2, "sum" : 3, "multi" : 2 }
{ "_id" : ObjectId("4ee7bd7b4238f33265331782"), "type" : "hop", "aIndex" : 3, "bIndex" : 5, "sum" : 8, "multi" : 15 }
{ "_id" : ObjectId("4ee7bd7b4238f33265331783"), "type" : "hop", "aIndex" : 3, "bIndex" : 11, "sum" : 14, "multi" : 33 }
{ "_id" : ObjectId("4ee7bd7b4238f33265331784"), "type" : "hop", "aIndex" : 3, "bIndex" : 2, "sum" : 5, "multi" : 6 }
{ "_id" : ObjectId("4ee7bd7b4238f33265331785"), "type" : "hop", "aIndex" : 7, "bIndex" : 5, "sum" : 12, "multi" : 35 }
{ "_id" : ObjectId("4ee7bd7b4238f33265331786"), "type" : "hop", "aIndex" : 7, "bIndex" : 11, "sum" : 18, "multi" : 77 }
{ "_id" : ObjectId("4ee7bd7b4238f33265331787"), "type" : "hop", "aIndex" : 7, "bIndex" : 2, "sum" : 9, "multi" : 14 }
{ "_id" : ObjectId("4ee7bd7b4238f33265331788"), "type" : "step", "aIndex" : 1, "bIndex" : 5, "sum" : 6, "multi" : 5 }
{ "_id" : ObjectId("4ee7bd7b4238f33265331789"), "type" : "step", "aIndex" : 1, "bIndex" : 11, "sum" : 12, "multi" : 11 }
{ "_id" : ObjectId("4ee7bd7b4238f3326533178a"), "type" : "step", "aIndex" : 1, "bIndex" : 2, "sum" : 3, "multi" : 2 }
{ "_id" : ObjectId("4ee7bd7b4238f3326533178b"), "type" : "step", "aIndex" : 3, "bIndex" : 5, "sum" : 8, "multi" : 15 }
{ "_id" : ObjectId("4ee7bd7b4238f3326533178c"), "type" : "step", "aIndex" : 3, "bIndex" : 11, "sum" : 14, "multi" : 33 }
{ "_id" : ObjectId("4ee7bd7b4238f3326533178d"), "type" : "step", "aIndex" : 3, "bIndex" : 2, "sum" : 5, "multi" : 6 }
{ "_id" : ObjectId("4ee7bd7b4238f3326533178e"), "type" : "step", "aIndex" : 7, "bIndex" : 5, "sum" : 12, "multi" : 35 }
{ "_id" : ObjectId("4ee7bd7b4238f3326533178f"), "type" : "step", "aIndex" : 7, "bIndex" : 11, "sum" : 18, "multi" : 77 }
{ "_id" : ObjectId("4ee7bd7b4238f33265331790"), "type" : "step", "aIndex" : 7, "bIndex" : 2, "sum" : 9, "multi" : 14 }
{ "_id" : ObjectId("4ee7bd7b4238f33265331791"), "type" : "jump", "aIndex" : 1, "bIndex" : 5, "sum" : 6, "multi" : 5 }
{ "_id" : ObjectId("4ee7bd7b4238f33265331792"), "type" : "jump", "aIndex" : 1, "bIndex" : 11, "sum" : 12, "multi" : 11 }
has more
> 


なんか、全部出してくれませんでした…orz

では、ここで、"type"が"step"で、"sum"の値が7より大きくて、"multi"の値が15以下のものを検索してみましょう。
SQLでは、つぎのような感じですね。

SELECT
    *
FROM
    LIST
WHERE
    TYPE = 'step'
AND SUM > 7
AND MULTI <= 15
なお、クエリーにはJSONを利用します。
> db.list.find({"type" : "step", "sum" : {$gt : 7}, "multi" : {$lte : 15}});
{ "_id" : ObjectId("4ee7bd7b4238f33265331789"), "type" : "step", "aIndex" : 1, "bIndex" : 11, "sum" : 12, "multi" : 11 }
{ "_id" : ObjectId("4ee7bd7b4238f3326533178b"), "type" : "step", "aIndex" : 3, "bIndex" : 5, "sum" : 8, "multi" : 15 }
{ "_id" : ObjectId("4ee7bd7b4238f33265331790"), "type" : "step", "aIndex" : 7, "bIndex" : 2, "sum" : 9, "multi" : 14 }
> 
演算子には$で始まる記号を用いるんですね。 ちょっとHTMLに慣れていると、一瞬オヤっ!と思うかもしれません。 記号の向きが逆になっていることに気をつけてください。
数学記号JSON中で指定する記号
<&gt
<=&gte
>=&lte
>&lt


はい、というわけで、次回はGmongoに行きます。


2011年12月12日月曜日

恥ずかしくて今さら聞けない、GMongo講座 1

さて、昨日は恥ずかしくて今さら聞けない、mongodb講座 1などというものを書いたわけですが、今日は

恥ずかしくて今さら聞けない、GMongo講座 1


です。

みなさんお間違えのないようにお願いします。

GMongoって何ぞ?という方もいると思いますので、とりあえず、簡単に説明すると、

Groovyからmongodbにつなげるthin ドライバーです。

もっと詳しく知りたい?ググレカスりましょう。

導入

GitHubのアドレスはhttps://github.com/poiati/gmongoです。

ここからzipダウンロードしてpathを通してやってください。

なお、このライブラリーは標準のmavenリポジトリーにも登録されています。
mavenから使いたい場合は、次のように記述すると使えます。

    <dependencies>
        <dependency>
            <groupId>com.gmongo</groupId>
            <artifactId>gmongo</artifactId>
            <version>0.9.2</version>
        </dependency>
    </dependencies>


おっと、私のように頭がゆとり系の人にはmavenなんて難しいことを書いてはダメでした。

@Grapesでは次のような感じです。

@Grapes(
 @Grab(group='com.gmongo', module='gmongo', version='0.9.2')
)


Gradleでは次のような感じになります。

repositories {
    mavenCentral()
}

dependencies {
    groovy : 'com.gmongo:gmongo:0.9.2'
}


ちなみにJavaとの親和性の高いGroovyですから、Groovyで書かれたライブラリを使ってJavaで開発なんてこともできると思います(テキトー)

では、昨日と同じような感じで使ってみたいと思います。

クライアント起動インスタンス化


ホスト名とポート番号を渡してやるだけでインスタンス化できます。
MongoTest.groovy

import com.gmongo.GMongo
import org.junit.Before

    @Before
    public void setUp() {
        def mongo = new GMongo('localhost', 27017)
    }


これだけで、mongodbとの接続が完了します。

DBの選択


使うDBを選択します。

昨日のuse db名称ですね。
これはGMongoの次のメソッドを用いることで、DBインスタンスを取得できます。

MongoTest.groovy

import com.gmongo.GMongo
import org.junit.Before
import com.mongodb.DB

    @Before
    public void setUp() {
        def mongo = new GMongo('localhost', 27017)
        def db = mongo.getDB('mydb')
    }


レコードの登録


SQLで言うところのinsertですね。

昨日の例では、mydb.itemsというエンティティ(?)に入れましたので、同様のコードを書きたいと思います。

まずはエンティティの取得から。

MongoTest.groovy

import com.gmongo.GMongo
import org.junit.Before
import com.mongodb.DB

    @Before
    public void setUp() {
        def mongo = new GMongo('localhost', 27017)
        def db = mongo.getDB('mydb')
        def entity = db['items']
    }


えっ、これだけ!?

そうなんです。これだけなんです。

さすがGroovy、Groovyかわいいよ、Groovy。

あとは、このentityにデータを入れていけば良いのです。

昨日の例ではここで登録するデータの形式はJSONでしたが、

さすがGroovy、Hashでデータを入れられます。

MongoTest.groovy

import com.gmongo.GMongo
import org.junit.Before
import com.mongodb.DB

    @Before
    public void setUp() {
        def mongo = new GMongo('localhost', 27017)
        def db = mongo.getDB('mydb')
        def entity = db['items']
        10.times {
            entity.insert([type : 'data', value : it])
        }
    }


さて、ではデータが登録されているかどうかテストしてみましょう。
MongoTest.groovy

import org.junit.Test
import com.gmongo.GMongo
import org.junit.Before
import com.mongodb.DB
class MongoTest {
    @Before
    public void setUp() {
        def mongo = new GMongo('localhost', 27017)
        def db = mongo.getDB('mydb')
        def entity = db['items']
        10.times {
            entity.insert([type : 'data', value : it])
        }
    }

    @Test
    public void testSetUp() {
        def mongo = new GMongo('localhost', 27017)
        def db = mongo.getDB('mydb')
        def list = db.items.find()
        assert list.size() == 10
    }
}


では、テスト実行!


はい、通りました!


片付け


テストを通ったのは良いのですが、これこのままにしておくと、
後でテストこけます。

なので、テストデータを作成したら、 @Afterを使ってデータを削除しておきましょう。
MongoTest.groovy

import org.junit.Test
import com.gmongo.GMongo
import org.junit.Before
import com.mongodb.DB
import org.junit.After

class MongoTest {
    @Before
    public void setUp() {
        def mongo = new GMongo('localhost', 27017)
        def db = mongo.getDB('mydb')
        def entity = db['items']
        10.times {
            entity.insert([type : 'data', value : it])
        }
    }

    @Test
    public void testSetUp() {
        def mongo = new GMongo('localhost', 27017)
        def db = mongo.getDB('mydb')
        def list = db.items.find()
        assert list.size() == 10
    }

    @After
    public void tearDown() {
        def mongo = new GMongo('localhost', 27017)
        def db = mongo.getDB('mydb')
        db.items.remove([:])
    }
}



ちなみに、 @Afterで実行したコマンドはmongodbにおける次のコマンドと同じです。


> db.items.remove({})
> 


はい、以上、今日はここまで。

次回はまたmongodbに戻ります。


2011年12月11日日曜日

恥ずかしくて今さら聞けない、mongodb講座 1

とりあえず、 G* Advent Calendarに向けて目下ネタを作成中です。

というわけで、mongodbをいじることにしました。

なぜは聞かないでください。

たんなる思いつきだけで生きていますので…

インストールとサービスの起動


インストールは結構簡単です。

サービスの起動も簡単です。

Macの人


Mac Portsでインストールしました。オレは…


sudo port install mongodb


だったような…(テキトーなので忘れている

まあ、わからなくても、「Max mongodb インストール」とかでググればなんか出てきます。
そんなものです。

ちなみに、portでインストールした場合、ビルドに結構時間がかかりました。

MBA 11inch Core-i7 で2時間くらいかかります。

あと、portでインストールした場合、自動でサービスを起動してくれるコマンドの紹介とか書いてあります。

Windowsの人


とりあえず、mongodbのホームページからzipをダウンロードして適度なフォルダにインストールしましょう。

インストール完了したら、mongod.exeとかいうのをダブルクリックすれば多分サービスが起動します。

Linuxの人


え、オレが教えなくても(ry
sudo apt-getとかでいいんじゃね(テキトー


とりあえず使う


クライアントを起動


とりあえず、使うには次のコマンドを叩いて、クライアントを起動しましょう。


$ mongodb
MongoDB shell version: 2.0.1
connecting to: test
> 


DBの選択



> use mydb
switched to mydb
> 


とりあえずデータを入れてみよう



> db
mydb
> db.items
mydb.items
> db.items.insert({"type" : "data", "value" : 1})
> assert(db.items.find().length() === 1, "insert 1 record, the number of fetch will become 1")
> 


はいデータ1件入っていますね。

とりあえず今日はここまで。



JIRA Advent Calendar 11日目

突然のムチャぶり


昨日の夕方頃、ベトナムのホテルでだらだらとしていたときにこんなツイートが…


yuskey
山本裕介

@mike_neck 明日 #jiraadvent がんばってください atnd.org/events/22899 /cc @Sean_SF


ん、オレ、まだJIRA買っていないんだが…困ったな。

で、よく見ると順番が大貫さんの次になっているし、エントリー内容もみんなレベル高い(・_・;)

すごい困った。





まあいいか。

JIRA Advent Calendarなのですが、あえてここでは、Confluenceの話でもしようかなと思います。

題して、

Confluenceを使ってみたい!



先述の通り、まだJIRAもConfluenceも買っていないんですが、Confluenceすごい楽しそうだなと思った動画があるので、それをどうぞ。



RedmineやTracのWikiなどは、非常に使い勝手が良いのですが、図も入ったようなドキュメントを書くのには若干向いていない気がしています。
そういう場合は仕方がないということで、MS Officeなどの製品に頼るわけですが、これがまた難敵で、各自の端末で編集するのでコラボレーションが難しいと思っていたわけです。

で、この動画を見たとき、すごい高い編集性能を持ちながら、Webで共有できるという機能に驚いてしまったわけです。


さて


さて、Confluenceほんとうに使いたいなんて思っているわけで、今なら円高だから10ユーザーでも850JPY程度。
JIRAとあわせて1,700JPY、ついでだからAtlassian Universityで習いつつやるということで、申し込んでも3,400JPY。

意外とお得かもしれません。

はい、以上、夢を語ってみました。



まあ、でもJIRAでObjective-Cの学習をチケット立てて管理するのもありだな
というわけで、来年くらいにはAmazon EC2か自宅鯖にでも導入しようと思います。

さて、次回のJIRA Advent Calendarですが、えっと、オレのムチャぶりに大人対応していただいたしょうゆさんです。

2011年12月4日日曜日

herokuにJavaアプリをデプロイ…のはずが、mavenの勉強で終わってしまった件

herokuがJavaに対応ということで、試してみました。

結果ですが、

Mavenが全然わからないレベルなので、herokuにJavaアプリをデプロイするところで凄い時間がかかってしまった…


Herokuのガイド通りにアプリを作成して、デプロイしたところ…


[INFO] ------------------------------------------------------------------------
       [INFO] BUILD FAILURE
       [INFO] ------------------------------------------------------------------------
       [INFO] Total time: 6.396s
       [INFO] Finished at: Sun Dec 04 11:26:33 UTC 2011
       [INFO] Final Memory: 9M/490M
       [INFO] ------------------------------------------------------------------------
       [ERROR] Failed to execute goal org.codehaus.mojo:appassembler-maven-plugin:1.1:assemble (default) on project orz.mikeneck.heroku.first: A type incompatibility occured while executing org.codehaus.mojo:appassembler-maven-plugin:1.1:assemble: java.lang.String cannot be cast to org.codehaus.mojo.appassembler.Program
       [ERROR] -----------------------------------------------------
       [ERROR] realm =    plugin>org.codehaus.mojo:appassembler-maven-plugin:1.1
       [ERROR] strategy = org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy
       [ERROR] urls[0] = file:/app/tmp/repo.git/.cache/.m2/repository/org/codehaus/mojo/appassembler-maven-plugin/1.1/appassembler-maven-plugin-1.1.jar
       [ERROR] urls[1] = file:/app/tmp/repo.git/.cache/.m2/repository/org/codehaus/mojo/appassembler/appassembler-model/1.1/appassembler-model-1.1.jar
       [ERROR] urls[2] = file:/app/tmp/repo.git/.cache/.m2/repository/net/java/dev/stax-utils/stax-utils/20060502/stax-utils-20060502.jar
       [ERROR] urls[3] = file:/app/tmp/repo.git/.cache/.m2/repository/stax/stax/1.1.1-dev/stax-1.1.1-dev.jar
       [ERROR] urls[4] = file:/app/tmp/repo.git/.cache/.m2/repository/org/codehaus/plexus/plexus-utils/1.5.6/plexus-utils-1.5.6.jar
       [ERROR] urls[5] = file:/app/tmp/repo.git/.cache/.m2/repository/stax/stax-api/1.0.1/stax-api-1.0.1.jar
       [ERROR] Number of foreign imports: 1
       [ERROR] import: Entry[import  from realm ClassRealm[maven.api, parent: null]]
       [ERROR] 
       [ERROR] -----------------------------------------------------
       [ERROR] -> [Help 1]
       [ERROR] 
       [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
       [ERROR] Re-run Maven using the -X switch to enable full debug logging.
       [ERROR] 
       [ERROR] For more information about the errors and possible solutions, please read the following articles:
       [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/PluginExecutionException
 !     Failed to build app with Maven
 !     Heroku push rejected, failed to compile Java app


おそらくmavenをちゃんと知っている人なら、たやすく回避できるんだろうな。

で、ググっていたら同じ問題を抱えていた人のブログを発見。

herokuのガイドのmavenの記述に誤りがあるらしいです。



<build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>appassembler-maven-plugin</artifactId>
                <version>1.1.1</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals><goal>assemble</goal></goals>
                        <configuration>
                            <assembleDirectory>target</assembleDirectory>
                            <generateRepository>false</generateRepository>
                            <programs>
                                <program>
                                    <mainClass>HelloWorld</mainClass>
                                    <name>webapp</name>
                                </program>
                            </programs>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>





<build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>appassembler-maven-plugin</artifactId>
                <version>1.1.1</version>
                <configuration>
                    <assembleDirectory>target</assembleDirectory>
                    <programs>
                        <program>
                            <mainClass>orz.mikeneck.heroku.first.HerokuServer</mainClass>
                            <name>webapp</name>
                        </program>
                    </programs>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>assemble</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>


まとめると、こんな感じです。

  • mavenではコンパイラーの指定がデフォルトで1.3になっているので、1.6に変更
  • appassembler-maven-pluginの記述が微妙に間違っている





2011年10月30日日曜日

Groovy Curry化の勉強のはずがっ Scope の調査に…

スコープってどうなってるの?



どうやら、この前書いたエントリで不思議に思ったことがあったので、
ちょいと調べて見ました。

それはクロージャーを返すクロージャーで宣言されているあの数値、
スコープはどうなっているのかということです。

試してみましょう。

scope.groovy

def scopedCounter = {
    def c = 0
    return {c+=1}
}

def sCnt = scopedCounter()
def tCnt = scopedCounter()
(1..10).each {
    assert sCnt() == it
    assert tCnt() == it
}



特に問題は発生しませんでした。
したがって、scopedCounterにて定義されている
変数cのスコープは
それぞれのクロージャーに閉じているようです。


そういえばっdef外せるのかな?



こんなことを考えなければ、よかった。
しかし、考えてしまったので仕方がない、調べてみました。


unscope.groovy

def unscopedCounter = {
    cnt = 0
    return {cnt+=1}
}

def uCnt = unscopedCounter()
def vCnt = unscopedCounter()
def escape = 0
(1..10).eachWithIndex { value, indx ->
    escape = indx
    assert uCnt() == value + escape
    assert vCnt() == value + escape + 1
}
assert cnt == 20



失敗すること3回くらい。
やっとなんとかつかめてきました。
クロージャーの中に使われている変数の宣言でdefを省略すると、
そのスコープは一つ大きなグローバルに含まれるようになる。

書き方を正すなら、先のコードは次のコードと同じ意味になる。


unscope.groovy

def cnt = 0
def unscopedCounter = {
    cnt = 0
    return {cnt+=1}
}

def uCnt = unscopedCounter()
def vCnt = unscopedCounter()
def escape = 0
(1..10).eachWithIndex { value, indx ->
    escape = indx
    assert uCnt() == value + escape
    assert vCnt() == value + escape + 1
}
assert cnt == 20


まず、グローバルスコープでcntを宣言しておいて、
その後、クロージャーunscopedCounterが呼ばれるたびに、0が設定される。
では、そのあたり本当か試してみましょう。


unscope.groovy

def unscopedCounter = {
    cnt = 0
    return {cnt+=1}
}

def uCnt = unscopedCounter()
def vCnt = unscopedCounter()
def escape = 0
(1..10).eachWithIndex { value, indx ->
    escape = indx
    assert uCnt() == value + escape
    assert vCnt() == value + escape + 1
}
assert cnt == 20

def wCnt = unscopedCounter()
assert cnt == 0
(1..10).each {
    assert wCnt() == it
}
assert cnt == 10



はい、通りました。
予測は正しかったようです。


結論

クロージャーを返すクロージャーを書く際には、戻り値となるクロージャーの中に使われる変数は極力元のクロージャーにて定義した変数を使うようにすること。



2011年10月27日木曜日

Curry化のお勉強の入門1

Curry化くらいスクリプト言語やっているんだから、出来るよね


なんてことを言われたら、困るので、カリー化のお勉強をしておこうと思う。

カリー化とは?


ググッてください

部分適用とかもよくわからないんで、どうしようかな


というわけで、Javascriptではよく使われている、
変数のprivate化をやって見ることにしましょう。
カリー化はその先にある。

多分。

counter.groovy

def counter = {
    def cnt = 0
    return { cnt += 1 }
}

def cnt = counter()

10.times {
    def value = cnt()
    println value
    assert value == it + 1
}


実行結果は次の通り

1
2
3
4
5
6
7
8
9
10


さて、オレが最初にカリー化を覚えたのは実はjavascriptの方が最初でして、やはりここはjavascript大先生に登場願わなければならんとせんといかんであるで御座候。

counter.js

var counter = function() {
    var c = 0;
    return function() {
        c += 1;
        return c;
    }
}
var cnt = counter();
for (var i = 1; i <= 10; i += 1) {
    console.assert (cnt() == i);
}
はい、これをChrome14.0.835.hogehogeで実行したところ、Assertion Errorは発生しませんでした。 というわけで、今回の目的、クロージャー内に変数をprivateとして隠蔽するという目標は達成。 では、明日はおもむろにカリー化に挑戦する!

2011年10月23日日曜日

GradleファイルからIDEAプロジェクトをつくろう

GradleからIDEAプロジェクトを作る


今、はやりのm2.eclipseなどのMaven~Eclipse関連のプラグインは充実してきています。

ただ、私、個人的にEclipseが好きでないし、
(いちいちコード補完を呼び出すのに、Ctrl + Spaceを押さなければならないとか、結構面倒)
Mavenなんてほとんど知らないので、
m2.eclipseとかを使っても、なんのメリットも享受できなかったりする…orz

というわけで、GradleとIntelliJ IDEAに走るわけですが、
つい最近知ったGradleのプラグインに便利なのがあったので、メモ。

例:jetty-embeddedなアプリケーションをGroovyと共に作成する場合の依存関係を解決するbuild.gradleを書いてみました。

build.gradle

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

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.eclipse.jetty:jetty-server:7.5.3.v20111011'
    groovy 'org.codehaus.groovy:groovy:1.8.3'
    testCompile 'org.codehaus.groovy:groovy:1.8.3'
    testCompile 'junit:junit:4.9.'
}


とりあえず、このファイルをプロジェクトの直下に置いて、次のコマンドを実行すると、
依存関係を解決しつつ、IDEAプロジェクトが作成されます。


$ gradle idea
:ideaModule
:ideaProject
:ideaWorkspace
:idea

BUILD SUCCESSFUL

Total time: 5.482 secs
$ 


オレの環境ではすでにライブラリーのダウンロードは終わっているので、特に表示されていませんが、
ライブラリーが足りない場合にはちゃんとダウンロードしてきます。

ダウンロードしたライブラリーは
/Users/username/.gradleの下に保存されています。

また、IDEAのライブラリーに自然にリンクが貼られている状態になるので、そのままIntelliJ IDEAでの開発を始められます。

というわけで、eclipseとmavenでなんかぎこちのない皆さん、Gradle + IDEAでプログラムの作成をやってみましょう。

以下、参考サイト

Gradle
Maven Search


2011年10月17日月曜日

JsonSlurperを始めよう!

仕事でWebApiの設計をしているので、
JSONを扱うことが非常に多くなってきました。

ただ、JSONを扱うといっても、
ドキュメントとしてJSONのサンプルを書いているだけで、
最後、Javaに直さなければならないことを考えると、
非常に無駄の多い作業だなと思うようになってきました。

そこでGroovyですよ!


DSLを書けばいいじゃんと考えるのが普通ですよね。

ただ、まあ、そうは言ったものの、
DSLを自作できるほどのスキルも無いので、

JSONをそのままPOJOファイルに変換・出力してくれるツールをGroovyで作れないか?

と考えて見ました。

ただ(え、まだなんかあるの?)、POJOにするツールといっても、
JsonSlurperすら使い方を知らない…orz

というわけで、JsonSlurperを試しに使って見ることにしました。

SampleJsonSlurper.groovy

import groovy.json.JsonSlurper

def json = $/
{
    "getAt" : "2011/10/17",
    "person" : {
        "privateId" : 12345,
        "name" : "hoge",
        "age" : 24
    },
    "boss" : {
        "privateId" : 0,
        "name" : "Jorge",
        "age" : 41
    },
    "depart" : "sales",
    "income" : 60000,
    "projects" : [
        {
            "id" : 123,
            "name" : "Idea",
            "income" : 300000
        },
        {
            "id" : 123,
            "name" : "Idea",
            "income" : 300000
        }
    ]
}
/$

def sluper = new JsonSlurper()
def object = sluper.parseText(json)

println object.getClass()

object.each {
    println it.key
    println it.value.getClass()
}



というわけで、ComplexタイプのJSONを作ってみて、それをパースしてみました。
出力結果は次のような感じです。


class java.util.HashMap
projects
class java.util.ArrayList
person
class java.util.HashMap
depart
class java.lang.String
income
class java.lang.Integer
getAt
class java.lang.String
boss
class java.util.HashMap


どうやら、JsonSlurperparseTextは、
HashMapを返してくれて、
各値にはそれぞれ、Stringとか、ArrayListとかを
返してくれているようですね。

というわけで、JSONからPOJOを作り出すのはなんとなくできそうな気がしてきました。



トレイトについて

さて、プログラミング言語Scalaにトレイトという機能があります。

…オレはよく知りませんが…

で、『東大英単』を読んでいたときに、「trait」という単語があったので、
それについて記事を書いて見ることにしました。

trait
意味
a distinctive characteristic
例文

The gray fox possesses a unique trait that distinguishes it from other members of the dog family: It is anatomically adapted for climbing.

ハイイロギツネは他のイヌ科とことなる特徴を持っている。高所によじ登ることが出来る身体能力を持っているのだ。


というわけで、何か他のものとは異なるという特徴を表すときにトレイトという言葉が使われるようです。

たしかScalaのトレイトはJavaのインターフェースのようなものですが、
若干異なるような感じがしますね。






2011年10月16日日曜日

It's not technological article.

Ryo (@ryoasai74) is now tweeting in English.
So it makes me decide to tweet in English too.

There are some reason to use English.
  • I'm recently working and collaborating with Vietnamese developer, communicating in English.
  • There are growing opportunities to read technological documents written in English.

So, these are forcing me to use English

But I have not used English often these days, I decided to read the book named 'Todai Eitan' published by the university of Tokyo.


『東大英単』 written and edited by Masako Notoro, Blendan Willson, Shiro Yamamoto, Tom Garry, Shoichiro Kawai, Shuichi Watabe.

civilization


In the first chapter of this book, I found the word 'civilization'.

The word 'civilization' means "a state of advanced learning, technology, or social organization/ a society possessing the same"

And there are some examples.

  • Colonialists often see themselves as bringing civilization to less fortunate peoples.
  • The society which produced the pyramids certainly deserves to be called civilization.

To translate these sentences into Japanese, it may be like these.
  • 移住者たちは自分たちが洗練された社会を恵まれない人に与えてやっていると思い込んでいたりする。
  • ピラミッドを作った社会は文明と読んでもいいだろう

This translation is not perfect...
But these sentences have much more explanation for other idioms.

The first example

There are two cool expression.

  • one sees him as X
  • bringing something to someone

The second example

Cool expression.

  • Hakuho deserves to be called real Yokozuna.


So this book has a worth reading.
To learn one word with some other idioms.

Hey! Why don't you read this book?

GroovyClassLoader凄いす

GroovyのGroovyClassLoaderが凄いので、ちょっとメモ


大したことのないエントリです。
Groovyって、まあ動的言語なわけですが、
さて、どうしたら動的にクラスとかを作り出せるのか、
はて?とおもっていました。

で、やっと見つけたのが、
GroovyClassLoader
です。

使い方はこんな感じ。

def loader = new GroovyClassLoader(this.getClass().getClassLoader())
def textClass = loader.parseClass(
$/
class DynamicPerson {
    def age
    boolean isAdult() {this.age >= 20}
}
/$
)
def dPerson = textClass.newInstance()
dPerson.age = 20
assert dPerson.isAdult() == true


文字列をClassとして読み込んでしまう、おそろしい機能ですね。

ちなみに、読み込むクラスは文字列なので、
当然のことながらコンパイルによる型チェックとかはしてくれません。
(IntelliJ IDEAでは試していませんが…)

ですが、まあ、Webアプリのプロトタイプを作るときなどは、
こういう機能があると、非常に助かる。

ちょっとしたエンベデッドなWebサーバーを作って、
Ajaxの適当なレスポンスを返すときには非常に便利ですね。

まあ、こんなのを書いていると、ぬこには怒られそうだけど…


第4回Jenkins勉強会に行って来ました

第4回Jenkins勉強会に行って来ました。



さて、内容の詳細ですが、
ブログまとめ職人の@shinyaa31さんが素晴らしいものを書いていますので、
そっち読んでください。

あと、トゥギャッターもまとめられているので、読んでみてください。

togetter - 10月15日 第4回Jenkins勉強会(東京都)
2011/10/15_第4回Jenkins勉強会( #jenkinsstudy )

当日のUST録画もありますので、興味のある方は御覧ください。

以上、報告終わり!


(´・ω・`)

2011年9月27日火曜日

Twitter謎の仕様について

久々にブログを書いているんだが…

Twitterの謎の仕様について


書いてみようと思います。

はるかぜちゃん(@harukazechan)っていますね。
彼女がツイートに顔を表象するときの記号を皆さんご存知だと思います。

(ω)

それを真似しようと思って、こんなツイートを書いてみました。

(m)


するとツイッターは何事もなかったのような反応を示しました。

おや、と思って、何度かやり直しましたが、全く反応なし。
そこで、普通にツイートしてみると、ツイートされる。

これは何だろうと思って、いろいろと試してみました。
そうすると、

(w)

や、

(M)

もダメなようでした。

そこでGroovyですよ


総当りで何がダメなのかを調べてみました。

ProhibitedTweet.groovy

@Grab(group='org.twitter4j', module='twitter4j-core', version='2.2.4')
import twitter4j.*
import twitter4j.auth.*
import twitter4j.conf.*

class Result {
    int id
    long status
    String expected
    String result
    String showResult(){
        return "success : ${expected == result}, id : ${id}, expected : ${expected}, result : ${result}"
    }
    @Override
    String toString() {
        return "<id : ${id}, expected : ${expected}, result : ${result}, status : ${status}>"
    }
}

def consumerKey = 'consumerKey'
def consumerSecret = 'consumerSecret'
def accessToken = 'accessToken'
def accessTokenSecret = 'accessTokenSecret'

def conf =
    new ConfigurationBuilder()
        .setOAuthConsumerKey(consumerKey)
        .setOAuthConsumerSecret(consumerSecret)
        .setOAuthAccessToken(accessToken)
        .setOAuthAccessTokenSecret(accessTokenSecret).build()
def twitter = new TwitterFactory(conf).getInstance()

def results = []
(65..90).each {
    def tweets = "(${it as char})"
    try{
        def result = twitter.updateStatus(tweets)
        results << new Result(id : it, expected : tweets, result : result.text, status : result.id)
    }catch(TwitterException e) {
        results << new Result(id : it, expected : tweets, result : '', status : -1)
    }
}

results.each { println it.showResult()}


実行結果は、こんなになりました。


success : true, id : 65, expected : (A), result : (A)
success : true, id : 66, expected : (B), result : (B)
success : true, id : 67, expected : (C), result : (C)
success : false, id : 68, expected : (D), result : (C)
success : true, id : 69, expected : (E), result : (E)
success : false, id : 70, expected : (F), result : (E)
success : false, id : 71, expected : (G), result : (E)
success : true, id : 72, expected : (H), result : (H)
success : true, id : 73, expected : (I), result : (I)
success : true, id : 74, expected : (J), result : (J)
success : true, id : 75, expected : (K), result : (K)
success : false, id : 76, expected : (L), result : (K)
success : false, id : 77, expected : (M), result : (K)
success : true, id : 78, expected : (N), result : (N)
success : true, id : 79, expected : (O), result : (O)
success : true, id : 80, expected : (P), result : (P)
success : true, id : 81, expected : (Q), result : (Q)
success : true, id : 82, expected : (R), result : (R)
success : false, id : 83, expected : (S), result : (R)
success : true, id : 84, expected : (T), result : (T)
success : true, id : 85, expected : (U), result : (U)
success : true, id : 86, expected : (V), result : (V)
success : false, id : 87, expected : (W), result : (V)
success : true, id : 88, expected : (X), result : (X)
success : true, id : 89, expected : (Y), result : (Y)
success : true, id : 90, expected : (Z), result : (Z)


結果としては、

(D)、(F)、(G)、(L)、(M)、(S)、(W)、(d)、(f)、(g)、(l)、(m)、(s)、(w)

を単独でツイートすることはできないということがわかりました。
これらをツイートすると、前のツイーとが返ってきます。

考察

さて、理由ですが、わかりません。
fとか、sはなんとなくわかりますが、
他のが全然わかりません。
(fはf**k、sはs*x)

顔文字とか、何かを想起するような記号なのかと考えましたが、
顔文字とかのように、文字によって何かを表すというのは日本語や中国語の表意文字体系での発想であり、
英語圏というかラテン語圏というか、インド何とか諸語の系統の表音文字体系にはない発想ですので、
それを禁止しているようにも思えません。

というわけで、なんでダメなんでしょうね。


2011年9月11日日曜日

Gradleでスローテスト問題を解決するAgain

前回、こんな記事書きました。
見事に効果がわからないという結果が出てきて、どうやればいいのか考えていましたが、
さすがGroovyクラスタに素晴らしい先輩がいらっしゃいました。

@bluepapa32 先輩です。
単純に、スリープするコード


    for (int i = 0; i < 100; i++ ) {
        Thread.sleep(10);
    }


を埋め込めばよかったわけですね。

というわけで、こんなテスト生成スクリプトでテストを大量に作ってみることにしました。


CreateTest.groovy

import static groovyx.gpars.GParsPool.*;
def packagePath = 'C:/Users/mike/IDEA_Project/GradleSample/src/test/java/orz/mikeneck/gradle/sample/boxunbox/test'

def head = $/
package orz.mikeneck.gradle.sample.boxunbox.test;
import org.junit.Before;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
/$

def body = $/
    public static final int SIZE = 100;
    private List<Integer> intList;
    private List<Long> longList;
    @Test
    public void testInteger()   throws InterruptedException {
        int[] array = new int[SIZE];
        int position = 0;
        for(Integer item : intList) {
            array[position++] = item;
            Thread.sleep(10);
        }
        for (int i : array)
            assertThat(i, is(intList.get(i)));
    }
    @Test
    public void testLong()  throws InterruptedException {
        long[] array = new long[SIZE];
        int position = 0;
        for (Long item : longList) {
            array[position++] = item;
            Thread.sleep(10);
        }
        position = 0;
        for(long item : array)
            assertThat(item, is(longList.get(position++)));
    }
    @Before
    public void setUp() throws Exception {
        Integer[] integers = new Integer[SIZE];
        Long[] longs = new Long[SIZE];
        for(int i = 0; i < SIZE; i++)
            integers[i] = new Integer(i);
        intList = Arrays.asList(integers);
        for (int i = 0; i < SIZE; i++)
            longs[i] = new Long(i + Integer.MAX_VALUE);
        longList = Arrays.asList(longs);
    }
}
/$

def numbers = []
(1..300).each {
    numbers << it
}

withPool {
    numbers.collectParallel { number ->
        def className = "BoxUnboxTest${number}"
        def name = "${className}.java"
        def fileName = "${packagePath}/${name}"
        def define = "public class ${className} {"
        def content = new StringWriter()
        content << head
        content << define
        content << body
        println ' ---- '
        println "now processing : $fileName"
        println ' ---- '
        new File(fileName).write(content.toString(), 'UTF-8')
        assert new File(fileName).exists() == true
    }
}


10msecを100回Sleepするテストメソッドが二つで、ひとつのテストの実行にかかる時間は2sec。
これが300個あるので、必要な時間は600sec = 10min かかるテストです。

実際に一つテストを実施してみました。


2secかかっていますね。
これが300個用意されるので、10minかかることが想定されます。

では、次のGradleスクリプトで実行してみましょう。


build.gradle

apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    testCompile 'junit:junit:4.8.2'
}



なお、マシンの環境は次のような感じです。
OS : Windows 7
CPU : Intel Xeon X3460 (8Core) (64bit)
RAM : 8.00GB




では、実行、というかその間に色々と他のことで負荷がかからないように、
IntelliJ IDEAが起動していて、このブログだけがChrome上で起動しているという状態にしておきます。
で、IntelliJのRunツールで実行するのではなく、ふつうにコマンドから起動します。

さて、オレはその間、『JOJOの奇妙な冒険』を読むこととします。

では、いざ実行…




c:\Users\mike\IdeaProjects\TestingGradle>gradle build
:buildSrc:compileJava UP-TO-DATE
:buildSrc:compileGroovy UP-TO-DATE
:buildSrc:processResources UP-TO-DATE
:buildSrc:classes UP-TO-DATE
:buildSrc:jar UP-TO-DATE
:buildSrc:assemble UP-TO-DATE
:buildSrc:compileTestJava UP-TO-DATE
:buildSrc:compileTestGroovy UP-TO-DATE
:buildSrc:processTestResources UP-TO-DATE
:buildSrc:testClasses UP-TO-DATE
:buildSrc:test UP-TO-DATE
:buildSrc:check UP-TO-DATE
:buildSrc:build UP-TO-DATE
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar UP-TO-DATE
:assemble UP-TO-DATE
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test
:check
:build

BUILD SUCCESSFUL

Total time: 15 mins 42.983 secs


15分43秒、マジ遅え。
しかも、最初の数個目までのテストは結構サクサク進んでいたけど、200を越えたあたりからテストの実行速度が落ちてきた感じがする。
こういう時はJVMの再起動などが必要なのかもしれん。

というわけで、さっそく、並列処理を試してみようと思う。

build.gradle

apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    testCompile 'junit:junit:4.8.2'
}

test {
    maxParallelForks = 6
    forkEvery = 30
}



前回にも書きましたが、
  • maxParallelForksは並列するスレッド数。
  • forkEveryは指定した回数テストを実行するとJVMを再起動して、OutOfMemoryExceptionを回避する仕組みです。

では、実行開始です。


c:\Users\mike\IdeaProjects\TestingGradle>gradle build
:buildSrc:compileJava UP-TO-DATE
:buildSrc:compileGroovy UP-TO-DATE
:buildSrc:processResources UP-TO-DATE
:buildSrc:classes UP-TO-DATE
:buildSrc:jar UP-TO-DATE
:buildSrc:assemble UP-TO-DATE
:buildSrc:compileTestJava UP-TO-DATE
:buildSrc:compileTestGroovy UP-TO-DATE
:buildSrc:processTestResources UP-TO-DATE
:buildSrc:testClasses UP-TO-DATE
:buildSrc:test UP-TO-DATE
:buildSrc:check UP-TO-DATE
:buildSrc:build UP-TO-DATE
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test
:check
:build

BUILD SUCCESSFUL

Total time: 2 mins 42.544 secs




えっ、2分Σ(゚д゚lll



めちゃくちゃ早くなっていません?
元々15分43秒かかっていたのがたったの2分43秒。

単純に計算すると

15min43sec / 6(core) = 2min37sec


となるから、妥当な値ですね。

では、テストのサマリーを比較してみましょう。



並列化前



並列化後



これを見るとCPU時間は変わっていません。
つまり、CPU時間をすべて複数コアで実行したために早くすることが出来たという事になります。

というわけで、結論

GradlemaxParallelForksを使うともれなくスローテスト問題が解決できる。