体験記/Oracle Certified Expert, Java EE 6 Web Component Developer (1Z0-899) 資格取得

先日、Oracle Certified Expert, Java EE 6 Web Component Developer (1Z0-899)(以下、ocej-wcd と略記) を取得しました。

僕は日本語で受験しました。

この資格を持っている人はけっこういるのだと思いますが、意外と検索に引っかからなかったので、興味を持っている方のお役に立つかもと思い、体験を書いてみます。

(下に中途半端な連載がありますが無視して)

背景

資格取得前の僕自身の背景です。

  • 一応プログラマ
  • 言語としてはJavaが一番仕事で使う
  • 専門領域
    • これと言って得意なものはない
    • 組み込み系だったり、Linuxデバイスドライバだったり、クライアントアプリだったり
    • 最近はjavaでWebサーバが多い
  • javaの素養
    • javaの実務経験はトータル5年くらいか
    • 一番最近のウェブ系は tomcat6 + seasar2
    • java ME CDC 1.1 + servlet 2.2 (?) がけっこう長かった
    • jspは実務経験なし。必要ないと思っている
    • java ee 6 実務経験なし
    • sjc-p 1.4は持っている。かなり前に取った

結果

  • 68% で合格
  • 一度目で合格

勉強方針

個人的な意見として、資格はあまり取りたくないと考えています。資格のための勉強時間がもったいないと感じるのと、資格の成果をモノづくりに活かせてないと感じているからです。ただ今回は、仕事の成り行き上取らざるを得なかったので取りました。

というわけで、ギリギリでいいので最小限の時間で取得することを目標にしました。

読んだ書籍

ocej-wcd の日本語の書籍は、現時点(2014-08-25)で僕の知る限り一冊もありません。なので、以下のふたつをやってみました。

まずひとつめ。

SUN教科書 Webコンポーネントディベロッパ(SJC-WC)(試験番号:310-083)

SUN教科書 Webコンポーネントディベロッパ(SJC-WC)(試験番号:310-083)

印象は以下。

  • Java EE 5 用だが
  • かなりよくまとまっている
  • 解説があって、章末に問題があり、最後に模擬試験という形式
  • 練習問題の難易度や雰囲気も本番に近い
  • 2周やった
  • 自分の弱い部分だけ手書きノートで抜き出した。20ページくらい

他の書籍を読んだことがないのですが、servlet/jspの参考書としても良い気がします。

ふたつめ。

徹底攻略Oracle認定Webコンポーネントディベロッパ EE 5問題集[CX-310-083]対応 (ITプロ/ITエンジニアのための徹底攻略)

徹底攻略Oracle認定Webコンポーネントディベロッパ EE 5問題集[CX-310-083]対応 (ITプロ/ITエンジニアのための徹底攻略)

  • これもJava EE 5用
  • この本はあまり良いとは思えない
  • 問題の性質が本番とかけ離れている
  • こちらの方は練習問題のみで、回答の解説の中で説明する形式
  • 1周目の途中で時間のムダが大きいと判断して中断した

Java EE 6。Java EE 5ではない

受験3日前くらいまでは、Java EE 5の勉強だけでいけるだろうと楽観視していました。この時点で上記のひとつめの書籍で正答率は85%程度。しかしネット上の模擬試験を行うと、Java EE 5だけではやはり歯が立たないかもしれないと感じはじめました。

そのとき活用したのは以下です。情報源は Passed OCEJWCD 6 and share my experience (certification results forum at Coderanch) が役立ちます。ただしすべて英語です。

こちらは言わずもがなですが、なんだかんだで役立ちます。上記の情報源を元に以下を読みます。

2.3.3.3 Asynchronous processing 
3.2 File upload 
8 Annotations and pluggability 
13.2 Declarative Security 
15.5 Annotations and Resource Injection 

ただ、全部読むのは時間がかかるので、はやめに模擬テストをしたいと考えました。
そこで以下が役立ちます。実際、仕様書を読むのを途中でやめてこれに移りました。

ここは簡易テストであれば無料で可能です。また\2,000程度でフルバージョンが入手できます。僕は購入しました。ウェブアプリ版もあるのでタブレットで手軽に学習できるのが良いところです。購入した価値はあったと感じます。ただし、問題難易度は本番より高めです。なので、本番想定の練習には微妙です。

受験本番

以下が受験本番に対する感想です。

  • 時間は充分。20分くらい残して退室した。結局は知ってるか知らないかなので。
  • Java EE 6は全体の3〜4割くらいの印象。
  • Java EE 5を100%取れるのであればJava EE 6はやらなくても受かりそう。

以上

ご参考まで。

GAEpでiPhone向けWebアプリを作る (11)ビューデータバインダを書く

前回の続きです。

ビュー<->データバインダを書く

サーバから受け取ったデータを、html ビューにバインドする処理を、javascript を使って書いていきます。

この処理には、cssjquery の組み合わせは非常に有用です。java では jsp があったり、django にもテンプレート言語があったりしますが、jquery を使うのであれば、テンプレートエンジンはもう必要ないのではないかという気もしてしまいます。

メリットとしては以下です。

  • html ファイルがそのままテンプレートファイル
  • 特殊な文法のテンプレートを覚える必要がない
  • html ファイルを開くだけで見た目がチェック可能
  • 保守が楽。変更したいときにすぐに変更できる
  • 通信はデータのみで済む。処理速度的メリット
  • ビューとデータの結びつきが疎になる

もちろんデメリットもあると思います。

  • css ベースの選択処理(id や class)がどうしてもグローバル変数的になる
  • 同様に、ビュー・データ・バインダの結びつきもうまくまとめないとわかりにくい
  • テンプレート継承が使えない
  • javascript のコードが多くなる

今回はあまりできていませんが、以下あたりが今後の課題になってきます。

これらはどれも関連するように感じます。特に、html / css のモジュール化は非常に重要になってくるだろうなと感じます。

今回のコードの全体はこちらです。

グローバル変数

今回は、ふたつのグローバル変数を用意しています。

  • ユーザ情報を格納する変数
  • ToDo 項目のリストを格納する変数

どちらもビューの作成をやりやすくするために導入してます。特にグローバル変数に持っていなくても問題ないと思いますが、通信量は減りそうだと思って入れています。

js/default.js内

var context = {
  'userinfo': {},
  'items': []
};
ユーザ情報のバインド

#list 画面にはユーザ情報を表示する箇所があります。

views.html の div#list タグ内

      <div class="userinfo">
        Welcome, <span class="user-nickname">guest</span>.&nbsp;

        (<a class="user-logout" href="/todo/main">logout</a>)
      </div>

jquery 的には最も単純な処理です。
js/default.js内

function setUserInfo(nickname, logoutUrl) {
  $('#list .userinfo .user-nickname').text(nickname);
  $('#list .userinfo a.user-logout').attr('href', logoutUrl);
}

例えば以下では、div#list タグの下の div.userinfo タグの下の span.user-nickname タグにニックネーム文字列を挿入しています。

  $('#list .userinfo .user-nickname').text(nickname);
ToDo 項目データを一覧でビューにバインド

次に、ToDo 項目のデータをリスト形式で表示します。
表示部の html は以下です。
views.html の div#list タグ内

      <ul class="todoitem-list" >
      </ul>

      <ul class="todoitem-template" >
        <li class="todoitem" >
          <span class="todoitem-delete" >Delete</span>

          <a class="todoitem-anchor" href="#save">
            <span class="todoitem-title">This is a todo item area.</span>
            <em class="todoitem-deadline">YYYY-MM-dd</em>
          </a>
          <div class="clear">
        </li>
      </ul>

この処理は refreshItems() 関数で行っています。

js/default.js内

function refreshItems() {
  // (1)
  var $todoparent = $('#list ul.todoitem-list');
  var $todoitemTpl = $('#list ul.todoitem-template li.todoitem:first');
  $todoparent.find('li.todoitem').remove(); // 既存データを削除

  // (2)
  for(var idx = 0; idx < context.items.length; idx += 1) {
    // (3)
    var $todoitem = $todoitemTpl.clone(); // テンプレートをコピー
    var data = context.items[idx];
    $todoitem.find('.todoitem-title').text(data.title);
    $todoitem.find('.todoitem-deadline').text(data.deadline);
    $todoitem.find('.todoitem-anchor').attr('id', data.key); // 項目キー
    $todoitem.removeClass('hidden');
    // (4)
    $todoitem.appendTo($todoparent); // 項目を追加
    $todoitem.find('.todoitem-anchor').click(function(data) {
      // (5) ToDo 項目を選択されたら
      return function(e) {
        var item = data;
        setItemToSaveForm(item); // #save 画面にデータをバインド
        transitTo('#save');      // 画面遷移実行
        return false;
      };
    }(data));
    $todoitem.find('.todoitem-delete').click(function(item) {
      // (6) 削除ボタンを押されたら
      return function(e) {
        var bDelete = confirm('Do you really delete it?');
        if(bDelete) {
          deleteItem(item.key, function(data, ts, xhr) {
            if(ts === 'success') {
              loadItems();
            }
          });
        }
        return false;
      };
    }(data));
  }
}

まずは、(1) で項目の挿入先($todoparent)と項目テンプレート($todoitemTpl)を取得し、挿入先を空にしてます。
それから、ループで(2)ひとつずつ ToDo 項目を追加していきます。
まずテンプレートのクローンを作成し(3)、グローバル変数内の項目をタグに割り当てます。この際、a タグに id を割り当てています。この id は、GAEp のモデルのキーです。これによってデータを識別します。
続いて(4)で、挿入先($todoparent)にいま作成したタグを追加します。
最後に、いま追加した項目に対してクリックハンドラを付けます。.todoitem-anchor タグには #save 画面への画面遷移、.todoitem-delete タグには ToDo 項目の削除処理を書いています。

ToDo 項目編集ビューにデータをバインド

最後、ToDo 項目編集ビュー (div#save 要素) にデータをバインドします。この処理は #list 画面からの画面遷移直前に呼ばれます(上記の(5))。
js/default.js内

function setItemToSaveForm(item) {
  if(item === undefined) {
    item = {'title':'', 'detail':'', 'deadline':'', 'key':''};
  }
  var $saveForm = $('#save form.todo-ctrl');
  $saveForm.find("input[name='todo-title']").attr('value', item.title);
  $saveForm.find("textarea[name='todo-detail']").text(item.detail);
  $saveForm.find("input[name='todo-deadline']").attr('value', item.deadline);
  $saveForm.find("input[name='todo-key']").attr('value', item.key);
}

内容に特に難しい内容はありません。単純に input 系タグにデータを入れているだけです。
todo-key という名前を持った hidden タイプの input 要素に GAEp のモデルキーを持たせてデータの識別をしています。
item === undefined の場合は、新規作成です。

次回は、サーバとのデータ通信関数です。

参考

jquery の入門としては、以前にも紹介した次の本が非常によいと思います。

iPhoneアプリケーション開発ガイド ―HTML+CSS+JavaScript による開発手法

iPhoneアプリケーション開発ガイド ―HTML+CSS+JavaScript による開発手法

javascript に関しては、有名ですが、以下が間違いなくよい本です。

JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス

JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス

僕は仕事柄 javascript を使うことはほとんどありませんが、それでも、この本がその手の世界では必須の本であろうことは、なんとなくわかります。javascript が楽しい言語だと言うことがよくわかります。何より読んでて楽しいです。

GAEpでiPhone向けWebアプリを作る (10)各画面を書く

前回の続きです。

今回はビューの作成です。普段仕事で html/css を使う機会がないので、その奥深さに戦きます。

各画面を書く

続いて、各画面の html/css を書いていきます。今回の ToDo アプリでは画面がふたつ存在します。

  • ToDo 一覧を表示する画面
  • ひとつの ToDo 項目の編集画面

最終的な html は、こちらcsstodo.cssiphone.css

ToDo 一覧画面

この画面の見た目は、以下の通りです。

この画面の html は以下の通りです。

views.html内

    <div id="list" >
      <div class="navBar">
        <h1 ><a href="main">ToDo list</a></h1>
        <a class="barButton right" href="#save">Add</a>
      </div>
      
      <div class="userinfo">
        Welcome, <span class="user-nickname">guest</span>.&nbsp;
        (<a class="user-logout" href="/todo/main">logout</a>)
      </div>

      <ul class="todoitem-list" >
      </ul>

      <ul class="todoitem-template" >
        <li class="todoitem" >
          <span class="todoitem-delete" >Delete</span>
          <a class="todoitem-anchor" href="#save">
            <span class="todoitem-title">This is a todo item area.</span>
            <em class="todoitem-deadline">YYYY-MM-dd</em>
          </a>
          <div class="clear">
        </li>
      </ul>
    </div>

まずは、ナビゲーションバー部分です。

      <div class="navBar">
        <h1 ><a href="main">ToDo list</a></h1>
        <a class="barButton right" href="#save">Add</a>
      </div>

タイトルと右ボタンを配置しています。タイトルをクリックするとリロードします。右ボタンをクリックすると"#save"画面に遷移します。

次に、ユーザ情報の表示部です。ナビゲーションバーのすぐ下にログインユーザ名とログアウトのリンクを表示します。

      <div class="userinfo">
        Welcome, <span class="user-nickname">guest</span>.&nbsp;
        (<a class="user-logout" href="/todo/main">logout</a>)
      </div>

特に難しい部分はありません。ログアウトのアドレスは適当です。あとで jquery で正しい値を入れます。そのために span タグと a タグには jquery で処理する用のクラス名を指定しておきます。

それから、一覧表示部分です。

      <ul class="todoitem-list" >
      </ul>

      <ul class="todoitem-template" >
        <li class="todoitem" >
          <span class="todoitem-delete" >Delete</span>
          <a class="todoitem-anchor" href="#save">
            <span class="todoitem-title">This is a todo item area.</span>
            <em class="todoitem-deadline">YYYY-MM-dd</em>
          </a>
          <div class="clear">
        </li>
      </ul>

この辺は少しトリッキーです。まず、実際の一覧表示部分は、"ul.todoitem-list" タグです。"ul.todoitem-template" タグには、リストアイテムのテンプレート "li.todoitem" を格納しています。
javascript で行う以下の処理を行う意図です。

  1. ToDo アイテムの個数分、ul.todoitem-template から li.todoitem タグをコピー
  2. Web API から得たデータを各 li.todoitem 内の要素にバインド
  3. ul.todoitem-list タグにその li.todoitem タグを挿入

"todoitem-template" クラスは css 内にて非表示にしてあります。

.todoitem-template {
  display: none;
}

また、"li.todoitem" のレイアウトにはけっこう苦労しています。iOS SDK でいうところの、UITableViewCell の UITableViewCellStyleSubtitle のスタイルにボタンを追加したレイアウトを再現しようとしています。

  1. [Delete] ボタンを載せて、右に寄せる (float:right;)
  2. 要素全体は a タグで1要素全体でリンクの働きをするように
  3. 1要素内でタイトルと締め切り(deadline)の2行となるように

もっときれいな css の書き方があるかもしれない。

ToDo 編集画面

この画面の見た目は、以下の通り。

こちらは特に難しい部分はありません。
この画面の html は以下の通り。

views.html内

    <div id="save"  >
      <div class="navBar" >
        <h1>Edit ToDo</h1>
        <a class="barButton back" href="#list">Cancel</a>
      </div>

      <form method="post" class="todo-ctrl" action="/todo/savereq" >
        <ul>
          <li>
            <label>Title:</label>
            <input type="text" name="todo-title" />
          </li>
          <li>
            <label>Deadline:</label>
            <input type="text" placeholder="YYYY-MM-dd" name="todo-deadline" />
          </li>
          <li>
            <label>Detail:</label>
            <textarea name="todo-detail"></textarea>
          </li>
          <li>
            <input type="hidden" name="todo-key" value="" />
            <input type="submit" value="Save" />
          </li>
        </ul>
      </form>
    </div>

「戻る」ボタンがついたナビゲーションバーと、あとは単純なフォーム送信画面です。特にこれと言って特殊なことはしていません。
強いてあげるなら、css で label タグの幅を 100 % にして、入力部分を次の行に折り返しているくらいです。


これでビューの作成は終わり。次回から javascript です。

参考

普通に iphone 向け Web アプリを作りたいならば、jqtouch を使うと楽です。オススメです。iphone っぽい見た目の部品もテーマとしてきれいにまとまっている印象です。

GAEpでiPhone向けWebアプリを作る (9)画面遷移の方法

前回の続きです。

もはや趣味では違うコードを書いているので、この記事のモチベーションを保つのが難しい。

画面遷移の方法

以前に書いた通りこのアプリのすべての主要な画面は、最初のアクセス時にすべてブラウザにロードするようにします。つまり、アプリ用の HTML ファイルは1枚のみです。

今回の ToDo アプリでは画面がふたつ存在します。

  • ToDo 一覧を表示する画面
  • ひとつの ToDo 項目の編集画面

これら複数の画面を javascript を用いてどのように画面遷移するか考える必要があります。今回はjqtouchを参考にしてそれを実現することにしています。

各画面を表す HTML

まず、以下のような1枚の html ファイルを作成します。

  • body タグの直下に複数の div タグを作成
  • このひとつひとつの div タグが、1画面を表す
  • 各 div タグにはそれぞれ id 属性を付ける
  • この id 属性が画面の名前を表す
<html >
  <head>
    <title>ToDo list</title>
    <!-- 略 ||-->
  </head>
  <body >
    <div id="list" >
        <!-- ToDo 一覧を表示する画面の HTML をここに書く -->
    </div>
    
    <div id="save" >
        <!-- ひとつの ToDo 項目の編集画面 の HTML をここに書く -->
    </div>

    <!-- 
       : 
       他に画面が必要なら同様に並べる
       : 
    -->

  </body>
</html>
画面遷移はアンカーリンクで

アンカーリンクのクリックで別画面へ遷移するようにします。
例えば以下のような感じです。

    <div id="list" >
          :

        <a href="#save">New</a>

          :
    </div>

上記の例は、一覧画面(list)内から新規作成画面(save)へ遷移するアンカーリンクを表します。

css/javascript による画面遷移処理

まず、css を準備します。

  1. css で、body タグ直下のタグはすべて display:none; にする。
  2. body タグ直下で有効な current という css クラスを用意し、これは display:block; にする。

つまり、以下のようにします。
todo.css

body > * {
  display: none;
  position: absolute;
  left: 0;
  width: 100%;
}

body > .current {
    display: block;
}

アプリロード時に(つまり、$(document).ready() で)、以下を行います。

  1. body タグ直下の一番上の タグに current クラスを付ける
    • つまり、一番上の div タグだけ表示する。それ以外のタグは display:none; なので不可視。
  2. href の値が '#' ではじまるアンカーリンクはすべてハイジャックし、画面遷移関数を呼ぶようにする

画面遷移関数では以下の処理を行います。

  1. body タグ直下のすべてのタグの current クラスをすべて削除
  2. 引数で渡された id のタグに対して current クラスを追加
  3. これにより画面遷移関数の引数に指定された id のタグだけが可視になる

言葉ではわかりにくいのでコードを書きます。
todo.js内

$(document).ready(function() {
  init();
});

function init() {
  var currentPage = undefined;
  // body タグ直下の一番上の タグに current クラスを付ける
  if($('body > .current').length === 0) {
    currentPage = $('body > *:first');
  } else {
    currentPage = $('body > .current:first');
    $('body > .current').removeClass('current');
  }
  $(currentPage).addClass('current');
  scrollTo(0, 0);

  // href の値が '#' ではじまるアンカーリンクはすべてハイジャックし、
  // 画面遷移関数を呼ぶようにする
  $("a[href^='#']").unbind('click');
  $("a[href^='#']").click(function(e) {
    e.preventDefault();
    transitTo($(this).attr('href'));
  });

  // :
  // :

}

// 画面遷移関数
function transitTo(viewid) {
  var next = $('body > ' + viewid + ':first');
  if(next) {
    // body タグ直下のすべてのタグの current クラスをすべて削除
    $('body > .current').removeClass('current');

    // +引数で渡された id のタグに対して current クラスを追加
    var currentPage = next;
    $(currentPage).addClass('current');
  }
}

これで画面遷移の用意ができました。

参考

今回はあえて使用していませんが、普通に iphone 向け Web アプリを作りたいならば、jqtouch を使うと楽です。オススメです。

GAEpでiPhone向けWebアプリを作る (8)続・リクエストハンドラをを実装していく

前回の続きです。
実装は簡単なのに話しが長くなってきて、いつまで続くのか不安です。

今回ここで作ったToDo アプリは、スケッチ用に作ってみたものですが、よくよく見回してみると ToDo アプリはけっこうあるもんです。Gmail も ToDo を管理する用途の機能を持ってるようですし、こないだ発表されたiOS5 でも Reminders と呼ばれる高機能な ToDo 管理アプリが入るようです。

GAEp 上にリクエストハンドラを定義する

前回長くなってやめた残りのふたつを実装していきます。

  • 特定の ToDo アイテムの作成/変更API
  • 特定の ToDo アイテムの削除API
SaveHandler の実装

SaveHandler では、以下のふたつの仕事をします。

  • ToDoアイテムの新規作成
  • 既存のToDoアイテムの編集

todo/handlers.py内

import datetime, time
import logging
from google.appengine.ext import webapp
from google.appengine.ext import db
from google.appengine.api import users
from todo.models import ToDoItem

# 〜〜〜

#
class SaveHandler(webapp.RequestHandler):
  #
  def post(self):
    logging.debug('SaveHandler#post')
    user = users.get_current_user()
    if user:
      req = self.request
      itemkey = req.get('todo-key')
      s_title = req.get('todo-title')
      s_detail = req.get('todo-detail')
      s_deadline = req.get('todo-deadline')
      dt_deadline = datetime.datetime.strptime(s_deadline, '%Y-%m-%d')
      item = None
      if(len(itemkey) != 0):
        item = db.Model.get(itemkey)
        item.title = s_title
        item.detail = s_detail
        item.deadline = dt_deadline;
      else:
        item = ToDoItem(
            owner=user,
            title=s_title,
            detail=s_detail,
            deadline=dt_deadline,
            )
      item.put()
    return

ユーザがログイン済みかどうかの処理は前回と同じです。

まず、入力の取得。

      req = self.request
      itemkey = req.get('todo-key')
      s_title = req.get('todo-title')
      s_detail = req.get('todo-detail')
      s_deadline = req.get('todo-deadline')
      dt_deadline = datetime.datetime.strptime(s_deadline, '%Y-%m-%d')

入力は、RequestHandler の request フィールドから get() メソッドで取ることができます。

deadline だけは文字列(YYYY-mm-dd形式)から datetime インスタンスに変換しています。
multipart/form-data がこんなにも簡単に取得できるのは素晴らしいと言うに尽きます。

リクエストが、編集なのか新規作成なのかは、todo-key があるか否かで判別しています。
todo-key がある場合は編集。

        item = db.Model.get(itemkey)
        item.title = s_title
        item.detail = s_detail
        item.deadline = dt_deadline;

編集の場合は、キーを元にデータストアから既存のレコード(エンティティ)を取得し、各プロパティに新しい値を代入しています。

todo-key がない場合は新規作成。

        item = ToDoItem(
            owner=user,
            title=s_title,
            detail=s_detail,
            deadline=dt_deadline,
            )

ToDoItem エンティティをコンストラクタで新規作成しています。

で、最後にコミット。

      item.put()
DeletionHandler の実装

最後に DeletionHandler 。ここでは既にある ToDo アイテムを削除します。

todo/handlers.py内

import logging
from google.appengine.ext import webapp
from google.appengine.ext import db

# 〜〜〜

#
class DeletionHandler(webapp.RequestHandler):
  #
  def post(self):
    logging.debug('DeletionHandler#post')
    itemkey = self.request.get('todo-key')
    item = db.Model.get(itemkey)
    if(item != None):
      item.delete()
    return

特に難しい部分はないです。
todo-key で削除対象のエンティティのキーを取得し、それを削除します。


ここまででサーバサイドの python コードは完了。
次からはクライアントサイドを書いていきます。

参考

Google App Engine については、基本的に Google が公開しているデベロッパーガイドを見ればだいたいのことはわかります。内容もわかりやすいです。

特に、Google App Engine Python については、以下です。

基本的にはこれらのサイトを見ればほとんどのことはわかりますが、その他に僕は以下の本を参考にしています。

Programming Google App Engine

Programming Google App Engine

和訳も出ているようです。

プログラミング Google App Engine

プログラミング Google App Engine

GAEpでiPhone向けWebアプリを作る (7)リクエストハンドラをを実装していく

前回の続きです。

リクエストハンドラを実装していく

続いて、GAEp のリクエストハンドラを実装していきます。

各実装ごとに部分的に書いていきます。

MainView の実装

MainView#get() では、単純に html を出力します。

todo/handlers.py内

import os
from google.appengine.ext.webapp import template

# 中略

#
class MainView(webapp.RequestHandler):
  #
  def get(self):
    s_tplpath = 'templates/views.tpl'
    path = os.path.join(os.path.dirname(__file__), s_tplpath)
    self.response.out.write(template.render(path, {}))
  • templates/views.tpl という名称のテンプレートファイルを http レスポンスとして出力しています。
  • テンプレートファイルと言いつつ、実はテンプレートではなく、html ファイルそのものです。データを埋め込みません。render() メソッドの第2引数は空オブジェクトです。
  • templates/views.tpl は、staticdir/views.html のシンボリックリンクになっています。
  • staticdir/views.html には、すべてのビューが含まれています。これは次回以降に。

GAEp のテンプレートエンジンは、デフォルトでは django のテンプレートエンジンとなっています。django のテンプレートエンジンのリファレンスは以下。

サーバサイドのテンプレートエンジンを利用したビューとモデルのバインディングはそれはそれでパワフルですが、今回はバインディングはクライアントサイドの javascript で行います。なので、ここで返すのは静的ファイルのみです。

ToDoList の実装

続いて、ToDoList#get() の実装です。

todo/handlers.py内

import logging
from google.appengine.ext import webapp
from google.appengine.api import users
from django.utils import simplejson as json
from todo.models import ToDoItem

# 〜〜〜

#
class ToDoList(webapp.RequestHandler):
  #
  def get(self):
    logging.debug('ToDoList#get')
    data = None
    user = users.get_current_user()
    if user:
      userinfo = {
        'nickname': user.nickname(),
        'logout_url': users.create_logout_url('/todo/bye.html')
      }
      q = ToDoItem.all()
      q.filter('owner =', user)
      q.order('deadline')
      results = q.fetch(20)
      itemlist = [{
          'key':str(item.key()),
          'title':item.title, 
          'detail':item.detail, 
          'deadline':item.deadline.strftime('%Y-%m-%d'),
        } for item in results]
      data = {
          'userinfo': userinfo,
          'items': itemlist,
        }
    self.response.content_type = 'application/json'
    json.dump(data, self.response.out, ensure_ascii=False)
    return

import 文は、この部分に必要なものだけ書いています。

処理の流れは以下です。

  1. アクセスしている人がログイン済み(user != None)だったら、
  2. そのユーザの20件の ToDo アイテムを古いものから検索し、レスポンス用オブジェクト(itemlist)に変換し、
  3. ToDo アイテム(itemlist)とユーザ情報(userinfo)をひとまとめ(data)にして
  4. JSON データとして http レスポンスに出力

アクセスしている人がログイン済みのユーザかどうかは、User クラスのインスタンスを取得することで確認できます。

    user = users.get_current_user()
    if user:
      userinfo = {
        'nickname': user.nickname(),
        'logout_url': users.create_logout_url('/todo/bye.html')
      }

取得は、google.appengine.api.users パッケージの get_current_user() 関数で行います。
リファレンスは以下です。

user != None ならログイン済み、そうでなければログインしていません。
また、ログアウト URL は、同じパッケージの create_logout_url() 関数で作成できます。この URL にアクセスすることで、ユーザはログアウトできます。引数には、ログアウト後のリダイレクト先 URL を指定できます。

以下の部分で、ユーザの ToDo アイテムを締め切りの古いものから20件検索しています。

      q = ToDoItem.all()
      q.filter('owner =', user)
      q.order('deadline')
      results = q.fetch(20)

で、検索結果をレスポンス用オブジェクト(itemlist)に変換しています。

      itemlist = [{
          'key':str(item.key()),
          'title':item.title, 
          'detail':item.detail, 
          'deadline':item.deadline.strftime('%Y-%m-%d'),
        } for item in results]

itemlist はディクショナリオブジェクトのリストです。ひとつの ToDo アイテムは、ひとつのディクショナリオブジェクトで表し、以下を持っています。

key
ToDo アイテムのキー。これには、ToDoItem エンティティのキーをそのまま利用する。
title
ToDoItem エンティティの title プロパティ(ToDo タイトル)の値
detail
ToDoItem エンティティの detail プロパティ(ToDo 内容詳細)の値
deadline
ToDoItem エンティティの deadline プロパティ(この ToDo の締め切り)の値。"2011-06-05" のように"年-月-日" という書式で。

で、最後に、ユーザ情報(userinfo)と ToDo アイテムのレスポンス用オブジェクト(itemlist)をひとつのオブジェクト(data)にまとめ、JSON 形式で http レスポンスに書き出しています。

      data = {
          'userinfo': userinfo,
          'items': itemlist,
        }
    self.response.content_type = 'application/json'
    json.dump(data, self.response.out, ensure_ascii=False)

GAEp には simplejson が入っているので、それを利用しています。ちょー簡単です。

長くなったので今回はここまで。

参考

Google App Engine については、基本的に Google が公開しているデベロッパーガイドを見ればだいたいのことはわかります。内容もわかりやすいです。

基本的にはこれらのサイトを見ればほとんどのことはわかりますが、その他に僕は以下の本を参考にしています。

Programming Google App Engine

Programming Google App Engine

和訳も出ているようです。

プログラミング Google App Engine

プログラミング Google App Engine

GAEpでiPhone向けWebアプリを作る (6)GAEp 上にリクエストハンドラを定義する

前回の続きです。

GAEp 上にリクエストハンドラを定義する

続いて、リクエストハンドラを定義していきます。
今回は、4つのリクエストハンドラを定義します。各リクエストハンドラの用途は以下です。

  1. ビューとなる html の取得
  2. ToDo アイテムのリスト取得API
  3. 特定の ToDo アイテムの作成/変更API
  4. 特定の ToDo アイテムの削除API

今回は実装しません。API だけ用意します。

リクエストハンドラのテンプレート作成

以下のようにします。

todo/handlers.py内

import logging

from google.appengine.ext import webapp

#
class MainView(webapp.RequestHandler):
  #
  def get(self):
    pass

#
class ToDoList(webapp.RequestHandler):
  #
  def get(self):
    logging.debug('ToDoList#get')
    return

#
class SaveHandler(webapp.RequestHandler):
  #
  def post(self):
    logging.debug('SaveHandler#post')
    return

#
class DeletionHandler(webapp.RequestHandler):
  #
  def post(self):
    logging.debug('DeletionHandler#post')
    return

各リクエストハンドラは、RequestHandler クラスのサブクラスで定義します。リファレンスは以下です。

各リクエストハンドラの役割は以下です。

MainView
ビューの取得用。ここにアクセスされるとメインとなるビューを html で返す。アクセスポイント
ToDoList
ToDo アイテムのリスト取得 API。このハンドラにアクセスしたユーザの最近の20個の ToDo アイテムをリストで返す。GET メソッドのみサポート
SaveHandler
特定のユーザの特定の ToDo アイテムの作成または変更を行う API。POST メソッドのみサポート。
DeletionHandler
特定の ToDo アイテムの削除を行う API。POST メソッドのみサポート。

それぞれ特に難しい部分はありません。

リクエストハンドラの URL マッピング

上記の各リクエストハンドラを、URL マッピングしておきます。

main.py内

import logging

from todo.handlers import MainView
from todo.handlers import ToDoList, SaveHandler, DeletionHandler

from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app


application = webapp.WSGIApplication(
                [
                ('/todo/main', MainView),
                ('/todo/list', ToDoList),
                ('/todo/savereq', SaveHandler),
                ('/todo/deletereq', DeletionHandler),

                ], debug=True)

def main():
  run_wsgi_app(application)

if __name__ == "__main__":
  main()

WSGIApplication クラスのコンストラクタで URL マッピングを行います。エイリアスとリクエストハンドラの組み合わせをタプルで結びつけたリストを教えるだけ。
レファレンスは以下です。

参考

Google App Engine については、基本的に Google が公開しているデベロッパーガイドを見ればだいたいのことはわかります。内容もわかりやすいです。

特に、Google App Engine Python については、以下です。

基本的にはこれらのサイトを見ればほとんどのことはわかりますが、その他に僕は以下の本を参考にしています。

Programming Google App Engine

Programming Google App Engine

和訳も出ているようです。

プログラミング Google App Engine

プログラミング Google App Engine