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の記述が微妙に間違っている