KLab若手エンジニアの これなぁに?

KLab株式会社の若手エンジニアによる技術ブログです。phpやRubyなどの汎用LLやJavaScript/actionScript等のクライアントサイドの内容、MySQL等のデータベース、その他フレームワークまで幅広く面白い情報を発信します。

2010年03月

制約があるユニットテスト方法その2

umjammer です、 携帯のサイト向けコードでは、時刻を扱うのにデバッグしやすいように仮想カレンダーが仕込まれておりメソッドの引数にも時刻が設定できたりします。この場合は問題ありません。 一方管理ツールでは現在の時刻を元に処理を行う場面が多く、コードの引数には時刻が存在せず今の時刻を前提にした処理が多く存在します。 今の時刻を前提にされると、テストに用いるデータも現在の時刻を前提にしないといけません。テストする時刻に合わせて毎回データを作るなんてやってられません。 # テスト駆動開発支持者からは時刻の引数をテストのために作っとけよと言われそうですが、 # 実際著名なライブラリでテストのために余計な引数を設けてるライブラリなんか # 見たことありませんよね?この辺は別の議論になるので割愛します。 で、どうするか? 今回はスクラッチされたコードではなく、時刻というオリジナルクラスの値を変更するので Java Instrumentation という、クラスのローディング時にバイトコードを書き換えられるという機能を用いて、オリジナルクラスである java.util.Calendar クラスが返す値を常に一定にするように書き換えてやりました。 バイトコードの書き換えは javassist を今回は使ってます。 こんな感じです、

public class TestInstrumentation {

    /** */
    private static ClassPool classPool;

    /**
     * @param agentArgs year,month,day
     */
    public static void premain(String agentArgs,
                               Instrumentation instrumentation) {
        classPool = ClassPool.getDefault();
        String[] args = agentArgs.split(",");
        final int year = Integer.parseInt(args[0]);
        final int month = Integer.parseInt(args[1]) - 1;
        final int day = Integer.parseInt(args[2]);

        instrumentation.addTransformer(new ClassFileTransformer() {
            /** */
            public byte[] transform(ClassLoader loader,
                                    String className,
                                    Class classBeingRedefined,
                                    ProtectionDomain protectionDomain,
                                    byte[] classfileBuffer)
                throws IllegalClassFormatException {

                if (className.equals("java/util/Calendar")) {
                    try {
                        ByteArrayInputStream stream =
                            new ByteArrayInputStream(classfileBuffer);
                        CtClass ctClass = classPool.makeClass(stream);

                        CtMethod ctMethod = ctClass.getDeclaredMethod(
                            "getInstance",
                            new CtClass[] { classPool.get("java.util.Locale") });
                        ctMethod.insertAfter(
                            "$_.set(" + year + ", " + month + ", " + day +
                            ", 12, 0, 0);" +
                            "System.err.println(\"javassist: fix calendar: " +
                            year + "-" + (month + 1) + "-" + day + " " +
                            "12:00:00" + "\");" +
                            "return $_;");

                        return ctClass.toBytecode();
                    } catch (Exception e) {
                        throw (IllegalClassFormatException)
                            new IllegalClassFormatException().initCause(e);
                    }
                } else {
                    return null;
                }
            }
        });
    }
}
テストするときは、これを使って時刻を固定するとテストデータもその時刻の物だけで済むようになります。 JVM 起動時には上記プログラムを jar にして VM オプションとして

  -javaagent:foo.jar=2008,5,14

などと、指定してやります。 その1の Aspect 使うのとどう違うの?と言われると、こちらの方が万能なので Aspect の方はこの方法で代替できます。ただこちらは VM の引数を増やしたり、jar を作っておかないといけないとか面倒なのと、いろいろな方法を紹介したかったので分けています。 また、この方法もバイトコードが書き換え可能な言語なら適用できますので、お試しください。

制約があるユニットテスト方法その1

umjammer です、 ユニットテストを行うに際して、本番で使うデータが存在するディレクトリとは違うテスト用のデータが入っているディレクトリを使用したい、といった場面でどうするか?という方法です。 本来なら、そんなのちゃんと設計して設定ファイルで変更できるようにしておけよ、と言われる場面ですが多人数が関わる大規模プロジェクトになるとそうは言ってられない事態に出くわすことが多々あります。他のメンバーが作ったクラスのユニットテストを後から作る場面も出てくる訳です。 で、どうするか? 今回はアスペクト指向プログラミングの実装である AspectJ を用いて実行時にメソッドが返す値を 書き換えてやりました。 実際のコードはこんな感じになります。 SpringAOP を使用した例 spring-beans.xml

  <aop:config>
    <aop:aspect id="testFooAspect"
                ref="testFooAroundAdvice">
      <aop:around pointcut-ref="aroundBar"
                  method="aroundBar" />
      <aop:pointcut id="aroundBar"
                    expression="execution(* org.klab.foo.Foo.bar(..))" />
    </aop:aspect>
  </aop:config>

  <!-- AroundAdvice クラスに書き換えたい値を DI -->
  <bean id="testFooAroundAdvice"
        class="org.klab.foo.AroundAdvice">
    <property name="wantToReplace"
              value="replace_value" />
  </bean>
AOP 本体のクラス

package org.klab.foo;

public class AroundAdvice {

    private String wantToReplace; // DI される書き換えたい値

    public void setWantToReplace(String wantToReplace) { // DI 用
        this.wantToReplace = wantToReplace;
    }

    public Object aroundBar(ProceedingJoinPoint pjp) throws Throwable {

        Foo foo = Foo.class.cast(pjp.getArgs()[0]);

        // wantToReplace を foo 内で書き換える        

        Object result = pjp.proceed(new Object[] { foo });

        return result;
    }
}
Java に限らず実行時にコードを書き換えられる言語だと可能な技なので、試してみはいかがでしょうか?

IBM基礎研究所様、KLab合同勉強会レポート

IBM様の社名を伏せなければいけないと勘違いしておりましたので一部内容を修正させて頂きました。 Fri Mar 12 17:25:37 JST 2010 稲田の発表のスライドをアップロードしました。 Wed Mar 17 14:27:00 JST 2010 はじめまして。suzuki-sと申します。 今年の1月にKLabに中途入社いたしまして、初めての投稿です。 これをきっかけにどんどん投稿していきたいと思いますので、今後ともよろしくお願いいたします。 さて、近頃KLabは勉強会ラッシュでして、先日のサイバーエージェント様に引き続き、 3月5日(金)、IBM基礎研究所の方々と勉強会をさせていただきました。 その様子をレポートさせていただきます。 まず、弊社からは稲田、小浦が発表をさせていただきました。

稲田:Bazaarについて

稲田の発表の様子


稲田からは自らがコミッタも務めているBazaar(bzr)に関する発表をさせていただきました。 分散バージョン管理についての説明から、bzrの優れている点、git, mercurialの優れている点の説明がありました。


残念ながら、基礎研究所の方々はgit, mercurialユーザの方のみでしたが、この機会にbzrも試していただけると稲田も喜ぶと思います。 KLabにはsvnやgitも混在していますが、稲田の働きかけによりだんだんbzrに移行しつつあるようです。 今後も精力的に活動してまいりますので、皆さま是非ともbzrをよろしくお願いいたします。 ちなみにWindows向けGUIのTortoize BZRの開発者募集中だそうです。

小浦:Apache 2.3探訪 mod_auth_form + mod_session

小浦の発表の様子


小浦からは、Apacheの新バージョンである2.3についての発表をさせていただきました。 新機能のひとつに、ユーザ認証をApache内部で実現するmod_auth_formがあります。 これについて、Apacheモジュールの動作原理から始まって、認証機能の実現方法、mod_sessionとの連携について発表しました。


さらに詳細な内容については、弊社ブログ「DSAS開発者の部屋」でも紹介しております。 興味のある方は是非以下もご覧ください。 Apache 2.3/2.4系に実装中の新機能をちょっと先取りして見てみよう Apache 2.3/2.4系に実装中の新機能をちょっと先取りして見てみよう その2 Apache 2.3/2.4系の新機能を見てみよう その3 ~MPMの動的ロード~ Apache 2.3/2.4系の新機能を見てみよう 番外編 ~イベントフックの実装~

最後に

懇親会では、ちょうど仕事で確率分布の話題があったので、発表者の方に質問をさせていただきました。 これもすごく参考になりました。ありがとうございました。 研究所の方々、といえば少しかたいイメージを持たれるかも知れませんが、皆さん気さくな方々で、話も弾みました。 技術的なお話だけでなく、IBM基礎研究所様ならではの仕事に対する考え方も伺うことができましたし、とても楽しかったです。 レポートは以上になります。 KLabでは今後もこのような勉強会を実施していきたいと考えております。 興味を持たれた方は、是非お気軽にお問い合わせください。 >IBM基礎研究所のみなさま 遠方よりお越しいただき、ありがとうございました。 普段なかなか聞くことのできない内容で大変新鮮でしたし、参考になりました。 弊社発表もみなさまにとって何か参考になるものでしたら幸いです。 よろしければ今度はこちらからお邪魔させていただきたいと思います! またの機会がありましたら、是非よろしくお願いいたします。

サイバーエージェント様・KLab合同勉強会レポート

どうも、honda-hです! 先日、「アメーバブログ」や「Amebaなう」などの面白いサービスを次々にリリースされているサイバーエージェント様と合同で勉強会をさせていただきました。 その様子をレポートさせて頂きます。 非常に有り難いことに予想を上回る参加人数となり、30人分用意していた席がほぼ埋まってしまいました。


KLabセッション その1 「ベイジアンフィルターによるユーザー投稿フィルタリングエンジン Ishigroid」

まずは弊社の高田による発表。


ユーザ投稿系のサービスの運営で問題となるのが、誹謗中傷、出会い目的、卑猥な書き込みといった投稿に対する対策です。 多くの場合、サイト運営者が人力で書き込みのチェックを行いますが、ほぼ24時間張り付かないといけなかったりしてすごく大変・・・。 Isigroidはベイジアンフィルタを用いて書き込みを自動的にチェックすることで、検閲のコストを下げようというものです。 テストの結果では全体の成功率(実際の検閲結果と一致した割合)が約86%で、ヤバイものにNG判定をした割合(positive(ng))が90%と、そこそこ使えそうな感じになっています。 発表資料はこちら↓


KLabセッション その2 「LL言語でもHudsonを使おう!」

続いて、弊社佐々木による発表。


継続的インテグレーションツール(CI)のHudson、それ自体はJavaで実装されていますが、「LL言語でもHudsonでCIできるよ!」というお話。 PHPのプロジェクトを例に、ソースがコミットされると自動的にプロファイリングしてくれるという、なんともクールな環境の構築例の紹介です。 そしてwebgrindというツールを使えばプロファイリング結果を可視化してくれるそうです。非常にカッコいい! そしてKLab社内ではおさかな君ネタがすっかり定着しました(笑)


発表資料はこちら↓


サイバーエージェント様セッション その1 「CGMサイトにおける絵文字の使用傾向」

続いてサイバーエージェントの大平さんによる発表。


WebDB Furumでバイドゥさんが発表された携帯向けページでの絵文字の使用傾向と、大平さんが調査された「Amebaなう」での絵文字の使用傾向の比較結果を紹介していただきました。 携帯向けページはサイトナビゲーションの目的で絵文字が使われていることが多い(ナビゲーションキーを示す番号のマークなど)のに対して、Amebaなうでは感情表現などの目的で使われることが多いという違いが興味深いです。サービスを考える上で非常にとても参考になりそうです。 同じユーザ投稿でも、ブログ、メールなど他のチャネルではどんな風に使われているか、個人的には気になるところです。

サイバーエージェント様セッション その2 「BaysieanSetsを用いた連想検索とアイテム推薦」

次は服部さんによる発表。


「レコメンドには膨大な計算量が必要」というのが私の認識でしたが、BaysianSetsを使うことで非常に高速に(リアルタイムに!)結果を得ることができるそうです。 そして、Wikipediaのデータを元にした連想検索のデモは非常にカッコよかったです!Google Sugest的なことができますね!

サイバーエージェント様セッション その3 「拡張現実感 Pigg」

最後は、福田さんによる発表。


現場系アイドルオタの福田さんとしては現場に行って体験するARにかなり思い入れがあるようで、その熱い思いがビンビン伝わってきました。 そしてAmeba Piggのアバターを使ったARのデモでは、オリンピックにちなんだトリプルアクセルで勉強会のフィナーレを飾りました!

最後に

サイバーエージェントのお三方とも、とてもプレゼンが上手くて刺激をいただきました! 技術的に面白いだけでなく、合間合間にユーモアを挟みつつ聴衆を惹きつける力は本当にすごいですね! また、みなさん技術が大好きという感じで、とても楽しそうに話をされていたのが印象的でした。 とても熱い技術者魂をお持ちで、これまた大いに刺激を受けました! サイバーエージェントのみなさま、今回はありがとうございました。 またぜひ一緒に勉強会やりましょう!

P.S.

今回発表してくださったサイバーエージェントの大平さんが、レポートを書いてくださっています。 勉強会の翌日にレポートをアップするスピード感は見習いたいです! 当日の雰囲気が良く伝わってきます。ぜひこちらもご覧ください! KLab×サイバーエージェント 合同勉強会|社内NEET宣言

Kindle Development Kit (KDK) アプリ一番乗りを目指す!

umjammer です。 Kindle Development Kit (KDK) が発表されてしばらく立ちます。 私も Java 使いとしては黙ってられなかったのですぐさまベータプログラムにエントリーしたのですが、3月に入ってもいっこうに知らせがありません。 審査があるみたいなので皆にいくとは思わないのですが、twitter で検索しても嘘くさい何件か以外は見当たりません。まだでてないんですよね? もう待っていられないので先に作っちゃいます。 ちなみに KDK は Javadoc がすでに公開されています。 世の中にはいろんなものが落ちていまして codavaj という Javadoc リバースエンジニアリングツールが存在します。codavaj ? Javadoc の逆かい!素敵なネーミングセンスです。 codavaj は Javadoc を読み込んで Java プログラムのスケルトンへ変換することが可能です。 KDK が手に入らないのなら自前で実装します!

$ codavaj.sh wget http://kdk-javadocs.s3.amazonaws.com/index.html docs
$ codavaj.sh codavaj docs src http://java.sun.com/javame/reference/apis/jsr217/

とすれば、src にコードが生成されます。 今回は KComponent ぐらいしか使わないのでその辺りと、KindletContext を適当に実装しました。 KDK の超適当実装 さて KDK はできました。次は Kindlet でアプリ作成です。 KDK は Personal Basis Profile ベースなので Kindlet 対応すれば他は殆ど Applet と同等なので

public class KindleTestApp implements Kindlet {

    private MyView view;

    private KindletContext context;

    /** */
    public void create(KindletContext context) {
        try {
            view = new MyView();
            this.context = context;
            this.context.getRootContainer().add(view); // ここ勝手に想像w
        } catch (Exception e) {
e.printStackTrace(System.err);
        }
    }

    /** */
    public void start() {
        new Thread(view).start();
    }

    /* */
    public void stop() {
    }

    /* */
    public void destroy() {
    }

    /** */
    private class MyView extends KComponent implements Runnable {
      :
こんな感じでしょうか? あとはビューアを適当にこしらえて
Kindle Emulator


一番乗り??? 実機は白黒だよなぁ... スキンを作って白黒にしてみました。
 KLab若手エンジニアブログのフッター