2010-03-27

GAE/J本買いました

GAE/J本「Google App Engine for Java [実践]クラウドシステム構築」を買いました。
発売は2009/9なので内容的には若干古いですが(メール受信に関する記述がない...リリースされたのが発売後なので当たり前ですが)、基本はしっかり押さえられていて GAE/J特有の制限について大変勉強になりました。
実際の開発では「Slim3」を使うことになるので直接扱う知識は多くはないけど、ベースをしっかり理解しておかないと何かトラブったときに困ってしまうので、この本でキチンと理解しようと勉強中です。

2010-03-26

「ひが」さんのインタビュー

Slim3開発者の「ひが」さんのインタビューがここに掲載されています。
「Slim3の設計哲学は、“Less is more”を実現すること」だそうです。
開発をしていると、とかく機能がテンコ盛りに成りがちで複雑な作りになってしまうので、これは肝に銘じておきたいですね。
アジャイルプラクティス」にも、設計、ソースを"シンプル"に保つことの大切さが書かれていました。
普段の開発では、
  • 今必要なものだけを作る
  • 開発のサイクル内でリファクタリング作業を必ず行う
といったことを心掛けています。

2010-03-23

Slim3 1.0リリースされました(^_^)

3/18にSlim3 1.0がリリースされました。
自宅PCが壊れているので今日まで気がつかなかった。(;_;)
会社でGAE/J上の開発案件があり、4月から開発開始予定だったのでグッドタイミングでした。

2010-03-18

自宅PCのディスプレイが壊れた?(T_T)

昨日、家に帰ってPC起動したらディスプレイに何も表示されない! (-_-;
電源ランプは点いているし、再度電源ボタンを押すと電源が切れる(シャットダウン?)ので、どうやらOSは正常に起動しているようだ。
ということはディスプレイが死んだ?
バックライトとか液晶自体に問題があってディスプレイが真っ暗な状態になっているみたいだ。
これからGAE/J上に自作アプリを作ろうと考えていたのに...
はぁ、出鼻をくじかれた感じ (;_;)

2010-03-13

GAE/J + Slim3 [はじめてのSlim3(5)]

今回でSlime3公式サイトの「Getting Started」チュートリアルは最後、「Listing tweets」で入力したtweetが一覧表示されるようになる。

  • まずはMVCモデルのViewの部分となる war/twitter/index.jspを修正する。<form>~</form>の後に下記コードを追加する。
    ここでやっていることは
    1. "forEach"でtweetListから要素を一つづつ取り出す
    2. その要素をh()でエンコードして表示
    3. <hr />で水平線を表示
    で、この部分に一覧表示されることになる。
    <c:forEach var="e" items="${tweetList}">
    ${f:h(e.content)}
    <hr />
    </c:forEach>
  • では http://localhost:8888/twitter/ にアクセスして表示してみる。
    ...
    一覧に何も表示されない、これはJSPで表示しようとしている"tweetList"が作成されていないからだ。
  • war/twitter/index.jspに対応するのはcontroller.twitter.IndexContorollerとなるので、このクラスで"tweetList"を作成することになる。
    ということで、まずはテスト(仕様)を書く。controller.twitter.IndexContorollerTest#run()の最後に下記を追加する。
    assertThat(tester.requestScope("tweetList"), is(notNullValue()));
  • それじゃ、テスト実行(もう慣れたかな?リズムに乗ろう!)
    結果は...Red(NG) なぜかというと、まだIndexControllerに"tweetList"を作成するコードを書いていないから、ということでこの段階では"正常"。
  • では、IndexControllerを修正しよう。
    ここで一つTips、eclipseに「moreUnit」プラグインがインストールされていれば"Ctrl+J"でテストクラスと対象クラスを行き来できるようになるので切り替えが楽チンです。
    修正後は下記のようになる。
    ここでやることは
    1. TwitterService#getTweetList()でTweetのリストを取得する。
    2. それをrequestScope("tweetList", tweetList)でアトリビュート"tweetList"にセットする。
    public class IndexController extends Controller {
        private TwitterService service = new TwitterService();
        @Override
        public Navigation run() throws Exception {
            List<Tweet> tweetList = service.getTweetList();
            requestScope("tweetList", tweetList);
            return forward("index.jsp");
        }
    }
  • ここでgetTweetList()が未定義というエラーが出る。そう、TwitterService#getTweetList()はまだ存在していない。getTweetList()を作ることになるのでが、その前にテストを作る(「テスト駆動」だね(^_-))
    TwitterServiceTestに下記のテストメソッドを追加する。ここで"getTweetList()"がエラーとなっているはず。
    @Test
    public void getTweetList() throws Exception {
        Tweet tweet = new Tweet();
        tweet.setContent("Hello");
        Datastore.put(tweet);
        List<Tweet> tweetList = service.getTweetList();
        assertThat(tweetList.size(), is(1));
        assertThat(tweetList.get(0).getContent(), is("Hello"));
    }
  • では続いてビルドエラーをなくすためにTwitterService#getTweetList()メソッドを作る。エラーの出ている箇所にカーソルを合わせて"Ctrl+1"を押す。
    "型TwitterServiceのgetTweetListメソッドを作成します"を選ぶ。
  • するとTwitterServiceのソースが開いてgetTweetListメソッドの雛形ができているので下記のように修正する。
    フィールド t を定義
    private TweetMeta t = new TweetMeta();
    getTweetListメソッドを次のように修正する
    public List<Tweet> getTweetList() {
        return Datastore.query(t).sort(t.createdDate.desc).asList();
    }
  • さあ、これでコンパイルエラーがなくなったので、テストを実行する。
    ...
    結果はGreen(OK)
    全てのテストを実行していない場合はこれが最後なので実行しておこう。
    全テストを実行してもさほど時間はかからないので、必ず全テストを実行するようにしよう。(ちょっとした修正が思わぬところに影響を及ぼすこともあるので)
  • テストがAll Green(OK)となっていることを確認して http://localhost:8888/twitter/ にアクセスして動きを確認してみよう。
    何か文字列を入力して[tweet]ボタンをクリックすると一覧が表示される。
  • GAEにデプロイしてアクセスしてみよう。ローカルと同じように動くのが確認できる。うーん、簡単すぎる(^_^;
  • これでTutorialが終了した。
    ここで、一通り終えてみての感想を...
    • 全般的に、サクサクとストレスなく進められて気持ちイイ
    • Slim3 DatastoreのAPIが「S2JDBC」のような"流れるようなインタフェース"でスラスラ書けて読みやすい
    • 「テスト駆動(TDD)」は、普段の開発(主に「Teeda」)で実践しているので、特に違和感なく進められる。
      未経験の人はぜひ実践を。「アジャイルプラクティス 第5章 作る前から使う」に書かれているがシンプルな設計になり易い
開発(特に仕事の場合)は、様々なストレス要因があってプログラミングに集中できないことが多い。集中できないとミスをおこす可能性も高くなるので、余計な事に気を使わずにテンポよくプログラミングが進められる「Slim3」は気に入りました。
 今度、会社でGAE/J上に構築する予定のシステムがあるので「Slim3」を使ってみようかと思います。
個人でもアカウントは作ってあるので家族で使うようなアプリを作ってみようかな。

2010-03-12

GAE/J + Slim3 [はじめてのSlim3(4)]

一日、間が空いてしまいました。m(_ _)m
隣の部署で大火事状態のプロジェクトがありヘルプにかり出されたので、更新する時間が取れなかった(;_;)

前回の続き、「Creating a form」の最後のところ...TwitterService#tweet()メソッドを作成してテストがGreen(OK)となった続きから。
  • 次に進む前にTwitterService#tweet()メソッドで何をしているのか確認してみる。
    public Tweet tweet(Map<String, Object> input) {
        Tweet tweet = new Tweet();
        BeanUtil.copy(input, tweet);
        Transaction tx = Datastore.beginTransaction();
        Datastore.put(tweet);
        tx.commit();
        return tweet;
    }
    • BeanUtil.copy()でTweetオブジェクトに入力値を設定している。
      このcopy()メソッドが便利で第一引数(Mapオブジェクト)のKey値と同じ名前の"第二引数のプロパティ"にValue値をセットしてくれる。(contentに値が設定されることになる)普通は一つ一つgetter()メソッドを使ってセットするコードを書くのだが、こういう単純作業は人がやるべき作業じゃないよね。
    • Datastore.put(tweet)でTweetオブジェクトをデータストアに格納している。
      App Engineデータストアを扱う場合「Datastore Java API」で説明されているように、JDO, JPA, 低レベりAPIのいずれかを使用することになるが、ここではSlim3のDatastoreを使っている。これはタイプセーフな低レベルAPIのラッパーということでJDO,JPAを使うより早いらしい。
  • では、続き...
    今作ったTwitterService#tweet()メソッドをTweetControllerから呼んで、入力された"tweet(つぶやき)"をデータストアに格納されるようにする。
    ということで、この仕様をTweetControllerTestに書く。既にテストメソッドrun()は作成されているので下記のように修正する。
    @Test
    public void run() throws Exception {
        tester.param("content", "Hello");
        tester.start("/twitter/tweet");
        TweetController controller = tester.getController();
        assertThat(controller, is(notNullValue()));
        assertThat(tester.isRedirect(), is(true));
        assertThat(tester.getDestinationPath(), is("/twitter/"));
        Tweet stored = Datastore.query(Tweet.class).asSingle();
        assertThat(stored, is(notNullValue()));
        assertThat(stored.getContent(), is("Hello"));
    }
  • テストクラスを修正したので、テスト実行...Red(NG)
    TweetControllerはまだ修正していないので、今の時点ではRed(NG)で"正常"
  • では、テストが通りようにTweetControllerを修正する。"return redirect(basePath)"の前に一行追加する。
    service.tweet(new RequestMap(request));
  • テストを実行して...はい、Green(OK)
  • では実際に動かしてみる。「GAE/J + Slim3 [準備]」で作成しておいた"Webアプリケーション"が起動されていることを確認して、ブラウザから http://localhost:8888/twitter/ にアクセスする。
  • テキスト欄に何か文字を入力して[tweet]ボタンをクリックする。何も変化はないけど、入力した文字列がデータストアに格納されているはず。
  • データストアの内容を確認するにはブラウザで http://localhost:8888/_ah/admin/ にアクセスする。これはローカル開発用のコンソールでデータストアやTask Queueの内容が確認できる。
  • "Datastore Viewer"をクリックして、[List Entries]をクリックすると、データストアに格納されているデータの内容が表示される。ここに先ほど入力した文字列のデータが表示されているはずだ。
あと残りは入力したtweetを一覧表示する処理をつくる「Listing tweets」だ、今度の土日でやろう。p(^^)g

2010-03-10

GAE/J + Slim3 [はじめてのSlim3(3)]

前回の続き、「Creating a form」の途中から...TweetControllerクラスのrun()メソッドを修正してテストがGreen(OK)となった続きから。
  • モデルクラスのTweetクラスを生成する。build.xmlのgen-modelタスクを実行し、モデル名に"Tweet"と入力すると以下のソースが作成される。
    • src/(ルートパッケージ)/model/Tweet.java
    • test/(ルートパッケージ)/model/TweetTest.java
  • 新しくソースを生成したのでテストを実行する...はい、Green(OK)(^_^)
  • この時点で、Tweetクラスはデータストアに格納するための必要最低限の属性(key, version, schemaVersion)しか持っていない。
    このクラスは"/twitter/index.jsp"から入力された「tweet(つぶやき)」を表すクラスなので、それに必要な属性(content, createdDate)を追加する。以下のフィールドを追加してgetter,setterも追加する。
    private String content;
    private Date createdDate = new Date();
  • ソースを修正したのでテスト実行...はい、Green(OK)だね(^_^)
    ここでテストクラスの作り方について...元サイトにも書かれていることだが、教科書的にテストクラスを作るとgetter, setter一つ一つにテストメソッドを作ることになるんだが、実際の開発現場ではそんな事してられない。(-_-)
    というのは、Tweetクラスのソースを見てみるとgetter, setterは、フィールドをそのまま返す、設定するとしているだけであり、問題が起こる可能性はまずない。このようなgetter, setterのテストメソッドをイチイチ作っていたら時間がいくらあっても足らない。
    ということで、実際の開発現場ではgetter, setterのテストに関しては何らかのデータ加工を行っている場合だけ作っています。...閑話休題
  • 次はサービスクラスTwitterServiceを作成する。このクラスはモデルのTweetクラスをデータストアに格納する役目を担うことになる。
    では、build.xmlのgen-serviceタスクを実行し、サービス名に"TwitterService"と入力すると以下のソースが作成される。
    • src/(ルートパッケージ)/service/TweetService.java
    • test/(ルートパッケージ)/service/TweetServiceTest.java
  • 新しくソースを生成したのでテストを実行する...今回もGreen(OK)
    ここまで何回かテストを実行してきたけど、Red(NG)となったのは確か一回だけ。Green(OK)となるのが分かっているのに何でイチイチ実行しなければならないんだ、と思うよね。たしかにTweetServiceのソースは中身空っぽだし、TweetServiceTestではnotNullValue()[nullでない]のテストなのでGreen(OK)になるのは当たり前だよね。
    でも、ここで大事なのは「テスト作成」-「テスト実行」-「ソース修正」-「テスト実行」のサイクルを回して"リズム"に乗ること。さあ、リズムに乗って次に進もう。
  • 次はTweetService#tweet()メソッドのテストを作る。テストメソッドは下記のとおり。
    @Test
    public void tweet() throws Exception {
        Map<string, object=""> input = new HashMap<string, object="">();
        input.put("content", "Hello");
        Tweet tweeted = service.tweet(input);
        assertThat(tweeted, is(notNullValue()));
        Tweet stored = Datastore.get(Tweet.class, tweeted.getKey());
        assertThat(stored.getContent(), is("Hello"));
    }
    はい、ここで「あれ?」と気づいた人(^_^)/
    ……
    そう、TweetService#tweet()メソッドはまだ存在していない、eclipseのエディタで"tweet"のところに赤い波線がついていて「メソッド tweet(Map) は型 TwitterService で未定義です」とコンパイルエラーになっている。
    これが「テスト駆動」!
    テスト=仕様を書く→テストが通る(仕様を満足する)コードを書く、という具合にテストによって駆動される。
  • では、TweetService#tweet()メソッドを実装しよう。
    eclipseでは簡単、
    • エラーの出てる箇所("tweet")にカーソルを合わせて"Ctrl+1"を押す
      Tweet tweeted = service.tweet(input);
    • ポップアップから"型 'TwitterService'のメソッド 'tweet(Map)'を作成します"を選択する。
    • TweetService#tweet(Map)メソッドが作成される
  • これでコンパイルエラーが無くなったので、テスト実行...Red(NG)(-_-)
    tweet()メソッドはeclipseが自動生成して
    "return null"
    となっているので
    "assertThat(tweeted, is(notNullValue()));"
    でNG、ということでこの時点では正常。(テストが通らないことを確認することも大事)
  • それでは、テストを通すようにtweet()メソッドを実装する。
    public Tweet tweet(Map input) {
        Tweet tweet = new Tweet();
        BeanUtil.copy(input, tweet);
        Transaction tx = Datastore.beginTransaction();
        Datastore.put(tweet);
        tx.commit();
        return tweet;
    }
    
  • では、テスト実行...Green(OK)
あともう少しなんだが、日付が変わったしまったので今日はここまで。
m(_ _)m

※追記[2010.3.10 22:00] ソースコード部分を見やすくしました。(ソースコードHTMLコンバーターを使わせて頂きました)

2010-03-09

GAE/J + Slim3 [はじめてのSlim3(2)]

前回の続きで「Creating a form
  • war/twitter/index.jspのbodyタグ内を下記のように編集する
    <p>What are you doing?</p>
    <form method="post" action="tweet">
    <textarea name="content"></textarea><br />
    <input type="submit" value="tweet"/>
    </form>
  • 続いてこのフォームをsubmitした際の処理を行うTweetControllerを生成する。build.xmlのgen-controller-without-viewタスクで"/twitter/tweet"と入力すると以下のソースファイルが生成される("without-view"なのでjspは生成されない)
    • src/(ルートパッケージ)/controller/twitter/TweetController.java
    • test/(ルートパッケージ)/controller/twitter/TweetControllerTest
  • テストを実行してGreen(OK)となることを確認する
  • 次にテストクラス:TweetControllerTestを修正する。TweetControllerクラスは/twitter/tweet からリクエストを受けて/twitter/にリダイレクトすることになるので、それをテストするように修正する。
    run()テストメソッドの最後の2行を下記のように修正する。
    assertThat(tester.isRedirect(), is(true));
    assertThat(tester.getDestinationPath(), is("/twitter/"));
  • ここからが「テスト駆動開発」の面白いところ。(^_^)/
    テストを実行してRed(NG)になることを確認する。
    これはまず対象クラスのテスト=仕様をコードで記述すること、そしてそのテストが失敗となることを確認することである。
    この時点でテストがOKとなってしまったら変だよネ。だから"正しく"失敗することを確認しなければならない。
  • では、次にテストが通るようにTweetControllerのreturn文を下記のように修正する。これでbasePath="/twitter"にリダイレクトされるようになる。
    return redirect(basePath);
    
  • これでテストが通るはず。テスト実行...よし!Green(OK)だ(^_^)v
このセクションは少し長いので、今日はここまで。

ついでに「テスト駆動」について。「テスト駆動」では基本的に下記手順で開発を進める。
  1. テストを書く
  2. テストを実行して結果(通常失敗(Red))を確認する
  3. コードを修正する
  4. テストを実行して成功(Green)することを確認する
  5. リファクタリングする
  6. テストを実行して成功(Green)することを確認する
実際にソフト開発作業で「テスト駆動」で行っているが、旧来の開発作業と比べて一番のメリットは、精神的に楽に開発できること。(^_^)
 旧来のやり方だとテストを始めるのは、一クラス全部コーディングしてから、または複数のクラスをコーディングしていきなり結合レベルのテストをしていた。
仕様通りに動作するかどうか分からない状態のまま(精神的にはものすごく不安な状態)黙々とコーディングし続け、いきなり単体テスト/結合テストを行う。こんなの動くわけないでしょ(-_-
それに精神的に不安で高ストレスの状態でコーディングしていたらミスを起こす可能性も高くなる。
 テスト駆動では、まずテスト(javaの場合はJUnit)を書く。"テスト"というよりは"仕様"をコードで表すと言った方がピッタリくるかな。このテストもいきなりたくさん書くのではなくて、テスト対象メソッド一つについてのテストを書く。(テスト要因が多い場合には、まず正常系、次に異常系という具合に進めていくと整理しやすい)
このように少しづつ進めることで一歩一歩確実に前に進めるので、精神的に非常に安定した状態で開発ができる。それと[テストを書く]-[テスト実施]-[コードを書く]-[テスト実施]-[リファクタリング]というサイクルを繰り返すことでリズムが出てきて、「ここまでOK」、「次...ここまでOK」という感じでスイスイ前に進み感覚になり、楽しくなる。
もう一つのメリットは「回帰テスト」が楽にできること。テストプログラムなので何十項目のテストも実行すれば一瞬で完了する。なので区切りのついた時やコミットする前などには必ずすべてのテストを実施してレベルダウン(デグレード)が無いことを確認するようにしている。

 ユニットテストの効用については2年位前に買ったお気に入りの本「アジャイルプラクティス 達人プログラマに学ぶ現場開発者の習慣」の[5章 アジャイルなフィードバック]に色々説明されている。
この本、行き詰まった時などに読み返して[アジャイル]の初心にかえるようにしている。もう何回読んだかな?(結構影響受けたので、いずれこの本について書こうかな)
開発者なら「達人プログラマー―システム開発の職人から名匠への道」と並んで必読の本なので、まだ読んでない人はぜひ読んでくださいナ。
(↑Amazonの"商品プレビュー"を設定してみた、書籍名にマウスカーソルを合わせると本の画像がポップアップ表示されますよ)

2010-03-07

GAE/J + Slim3 [はじめてのSlim3(1)]

Slim3でアプリを作ってみる。
Slim3「Getting Started」-「Creating a controller and a test」に沿って進める。
  • プロジェクトの名前を"slim3-blank"から変更する
  • (プロジェクトroot)/war/WEB-INF/web.xmlの下記箇所の"param-value"の値をルートパッケージ名に変更する
    <context-param>
        <param-name>slim3.rootPackage</param-name>
        <param-value>tutorial</param-value>
    </context-param>
  • build.xmlの"gen-controller"タスクを実行する
  • コントローラのパス入力のダイアログが表示されるので手順通り"/twitter/"と入力する
  • すると以下のファイルが生成される(コントローラクラスとそのテストクラス、それとJSP)
    • src/(ルートパッケージ)/controller/twitter/IndexController.java
    • test/(ルートパッケージ)/controller/twitter/IndexControllerTest.java
    • war/twitter/index.jsp
  • "Webアプリケーション"が起動していなかったら起動する
  • ブラウザで http://localhost:8888/twitter/ にアクセスする
  • Hello twitter Index !!! と表示されればOK
  • テストクラスもあるのでテストを実行してみると...もちろん、Green(OK)
ここまで「あっと言う間」、テストクラスも含めて"サクっ"と作れてしまうのは凄く楽チン(^_^)
余計なことに惑わされずに中身に集中できそうだ。

仕事で「Teeda」を使った開発をしているが、これと同じ感覚で"サクサク"と進められそうだ。

2010-03-06

GAE/J + Slim3 [準備]

まずは、開発環境の準備から。
基本的には「スタートガイド」 に書かれているとおりに準備すればOK

次はSlim3、公式サイトの「Getting Started」に沿って進める。
まずはslim3-blankプロジェクトをダウンロード or エクスポートして、eclipseのプロジェクトとしてインポートする。
  • ダウンロード: http://code.google.com/p/slim3/downloads/list
  • Subversionリポジトリからダウンロードまたはエクスポート:http://slim3.googlecode.com/svn/trunk/slim3-blank/

Getting a blank project」に従って設定を進めて、このページの最後の[7.Running the Project]でプロジェクトが正常に起動できるか確認してみる。
  • eclipse[実行の構成]で"Webアプリケーション"を選択して新規作成する。
  • プロジェクトにインポートしたslim3-blankを指定する
  • すると
    [引数]: Projects using App Engine 1.2.6 or later require a Java agent. Add this VM argument:
    というエラーが表示されたので、表示された通りに[引数]タブの"VM引数"に下記のように指定する。
    -javaagent:(GAE SDKインストールパス)\com.google.appengine.eclipse.sdkbundle.1.3.1_1.3.1.v201002101412\appengine-java-sdk-1.3.1\lib\agent\appengine-agent.jar
  • エラーが消えたので実行してみると、今度はコンソールに下記のようなエラーが表示された。
    警告: Your working directory, (C:\e\workspace\slim3-blank) is not equal to your 
    web application root (C:\...\workspace\slim3-blank\war)
    「"working directory"が違ってるぞ」ということなので、「実行の構成」の[引数]タブの作業ディレクトリで"${workspace_loc:slim3-blank/war}"を指定して再度実行してみた。
  • 今度はエラーが出ず正常に起動された。とりあえず、ここまではOK! (^^)

「Google App Engine for Java」と「Slim3」に手を出してみる(^_^)

GAE/J(Google App Engine for Java)」が面白そうなので手を出してみることにした。
Slim3」を使うとアプリ開発するのが楽しくなりそうなのでこれを使ってみる。
ということで、「GAE/J」+「Slim3」で何か作ってみる。

GMailの容量、その後(3)

久々にGMailの容量を気にして見てみたら 7430MB(3/6 14:00時点)になっていた。
「GMailの容量、その後(2)」の時点(20092008/1/4)で 6280MBだったから2年で1GB増えたことになる。
使用率が 3%だから、これは当分使いきれないネ(^_^;

ページのテンプレートを変更してみた。ベース色は緑系で変わらないけど少し雰囲気が変わったかな。