Wicketで始めるオブジェクト指向ウェブ開発

第5回 Twitterタイムラインで見るWicketのオブジェクト指向プログラミング(前編)

この記事を読むのに必要な時間:およそ 4 分

前回までで,Twitterにログインしタイムライン表示用ページに遷移するところまでは実現できました。今回は,いよいよTwitterタイムラインを表示します。

Twitterタイムラインは,Twitterユーザの発言をリスト上に表示します。HTML上で見ると,同じ構造のタグが発言数分,ずらりと並んだ形をしています。JSPでそのような構造を作るときには,Javaのfor文もしくは同じような動作をするカスタムタグを使って,タグを繰り返し表示することで実現しました。

Wicketでは,このような繰り返しリスト全体を「繰り返し項目コンポーネント」として扱います。それがListViewクラスです。

ListViewによるコンポーネントの繰り返し

ListViewはHTML要素を繰り返すときに使うコンポーネントです。ListViewに対応するwicket:idは,⁠繰り返したいHTML要素」につけます。たとえば,次のようにすれば,repeatViewというwicket:idのついた<tr>タグ(内側の<td>タグも含めた全体)を繰り返し生成することができます。

リスト1 twitterスクリーンネーム一覧を表示するためのHTML

<table>
  <tr wicket:id="repeatView">
    <td wicket:id="userName">twitterのスクリーンネーム</td>
  </tr>
</table>

この<tr>要素を繰り返すためのプログラムが、次のプログラムです。

リスト2 ListViewの使い方

ListView view = new ListView("id", new TwitterStatusListModel<List<Status>>()) {
  @Override
  protected void populateItem(final ListItem<Status> item) {
    //itemが行を表すコンポーネント
    //itemにコンポーネントをadd()することで,1行のコンポーネントを組み立てる
    Status status = item.getModelObject();
    item.add(new Label("userName", status.getUser().getScreenName()));
  }
}

ListViewがデータを表示する際には,自身のモデルのgetObject()を呼び出します。モデルはgetObject()呼び出しの結果としてListを返します。ListViewはListの要素ひとつひとつを使ってpopulateItem()メソッドを呼び出します。populateItem()メソッドの引数は常にListItemで,このListItemが表の1行を表します。上記例でいえば,ListItemは<tr>タグを表しています。まさに、wicket:idをつけたタグです。wicket:idをつけたタグが、ListItemとして繰り返しpopulateItem()メソッドに渡されるのです。

ListViewに渡しているモデル「TwitterStatusListModel」はList<Status>型オブジェクトを返します。ListViewはこのListからStatusオブジェクトを1つずつ,populateItem()メソッドに渡します。Statusは行を表すコンポーネントであるListItemのモデルから取り出すことができます。item.getModelObject()が、ListItemからStatusを取り出しています。

あとはStatusオブジェクトを使って1行分のコンポーネントを組み立てるだけです。ListItemにコンポーネントを追加することで、<tr>タグの子要素を組み立てることができます。

この例では,wicket:id="userName"に対応するLabelコンポーネントを適用しています。LabelコンポーネントはStatusオブジェクトから取得できるスクリーンネームを表示します。

LoadableDetachableModelでリクエスト中のキャッシュを行う

ListViewの作り方がおおよそ分かりました。しかし,TwitterStatusListModelのような、Statusのリストを返すモデルがなければこのプログラムは動きません。

サンプルコード内のjp.gihyo.wicket.page.simple.Timelineクラスでは、TwitterStatusListModelの代わりに次のようなプログラムでTwitterのステータスを取得しています。

リスト3 Twitterのステータスリストを返すモデル

IModel<List<Status>> statusModel = new LoadableDetachableModel<List<Status>>() {
  @Override
  protected List<Status> load() {
    try {
      Twitter twitter = AppSession.get().getTwitterSession();
      return twitter.getFriendsTimeline(new Paging(1, ITEMS_PER_PAGE));
    } catch (TwitterException ex) {
      MyTimeline.this.error(getString("canNotRetrieveFriendTimeline"));
      return Collections.emptyList();
    }
  }
};

Statusクラスは,twitter4jが提供するTwitterのステータスを表現するクラスです。TwitterタイムラインはListView<Status>によって表示しますので,必要なモデル型はIModel<List<Status>>です。ListViewは常に自分が使うオブジェクトのListをモデルに期待します。IModel<List<Status>>に適合するモデルであれば何でもかまいません。

ここでは,見慣れないクラスLoadableDetachableModelを使いました。

LoadbaleDetachableModelクラスは,リクエスト毎の初回アクセス時にデータをロードし,キャッシュするモデルです。一度キャッシュされたデータは,レスポンスが返るまでは使い回されます。Twitterのステータス取得はTwitterサイトへのリモートAPI呼び出しを伴いますので,非常に重い処理です。リクエスト中に何度も呼ばれるとアプリケーション全体が重くなります。LoadableDetachableModelはこのようなケースに便利なモデル実装です。

Statusの取得

LoadbaleDetachableModelのload()メソッドをオーバーライドすることで,データのロード処理を記述します。

サンプルでは,前回までに作ったAppSessionクラスのgetTwitterSession()メソッドを使って,ログイン時に生成したTwitterオブジェクトを取得し,getFriendsTimeline()メソッドでタイムラインを取得します。結果値はList<Status>ですので,モデルの型と一致します。

getFriendsTimeline()は失敗する可能性がある操作です。失敗した場合にはTwitterExceptionがスローされますので,キャッチしてエラーメッセージを表示します。ここでは,FeedbackPanelに情報を表示するerror()メソッドを呼び出しています※1)⁠

また,エラーの時でもモデルは何らかの値を返す必要がありますので,空のリストを返却しています。

このモデルをListViewに渡すことで,タイムライン表示リストを作ることができます。

※1
MyTimeline.this.error()という表記を見慣れない方もいるでしょう。この記法はJavaの匿名サブクラスとローカルクラス特有のもので,自分を取り囲む親オブジェクトのthisにアクセスするための記法です。もし単純にthis.error()と書いた場合,thisはLoadableDetachableModelオブジェクトを意味しますので,error()メソッド呼び出しはコンパイルエラーになります(LoadableDetachableModelにそのようなメソッドはないからです)⁠MyTimeline.thisを付けることで,MyTimelineオブジェクトのerror()を呼び出すことを明示しているのです。

著者プロフィール

矢野勉(やのつとむ)

フリーランスのプログラマ。Wicket-ja主催。

ウェブ・アプリケーションの開発を中心にさまざまな案件に関わってきました。現在はWicketによる開発を支援しています。

URLhttp://d.hatena.ne.jp/t_yano/

著書

コメント

コメントの記入