レベル: 中級 Brandon J.W. Smith (brandon+dW@16cards.com), Software Engineer, IBM Hanumanth R. Kanthi (kanthi@us.ibm.com), IT Architect,
IBM
2008年 1月 08日 Project Zero は、サービス指向アーキテクチャー (SOA) に準拠した Web 2.0 アプリケーションのアジャイル開発を焦点として単純化された開発プラットフォームです。そんな Project Zero が武器とするライブラリーのなかに、SQL クエリーを実行するための簡易 API があります。この記事では、これらの API を利用して単純なウィキを作成する方法を説明します。
始める前に
この記事は、読者に Project Zero についての基本的な知識があることを前提とします。まだこの前提を満たしていない方は、2 つの初心者向け記事、「Web アプリケーションのための RESTful なサービスを作成する」と「Get
started with Project Zero and PHP」を読むことから始めてください。Project Zero を使い慣れている方は、このプロジェクトをダウンロードして (「参考文献」を参照)、単純なアプリケーションを自力で作成できるはずです。
はじめに
Larry Wall 曰く、最善なのは、「簡単なことは簡単に、難しいことは可能にする」ことです。
データ・アクセスには、簡単なものから極めて複雑なものまで、さまざまなケースがあります。それ故に多くの開発者たちは、複雑なケースに対応できると同時に単純なケースを扱いにくくすることもない柔軟性の高いデータ・アクセス API を願ってやみません。まさにそんな願いを叶えるのが、Project Zero のデータ・アクセス API (zero.data) です。zero.data は Hibernate や Java™ Persistence Architecture のような抽象化レイヤーとして意図された API ではありません。この記事でこれから説明するように、zero.data は SQL をシン・ラッパーで囲むことによって、簡単なことを簡単に、難しいことを可能にすることを目的としたライブラリーです。一例として、単純なクエリーを実行し、その結果を Bean インスタンスのリストとして取得する方法をリスト 1 に示します。
リスト 1. zero.data を使用して Bean およびマップ・インスタンスのリストを返す場合
Manager data = Manager.create("myDb");
List<Person> results = data.queryList("SELECT * FROM person", Person.class);
List<Map<String,Object>> resultsMap = data.queryList("SELECT * FROM person");
|
去年 1 年間、Project Zero チームは IBM® の Information Management 開発者たちと緊密に連携して pureQuery に取り組みました。このハイパフォーマンス・データ・アクセス・プラットフォームには、Java アプリケーション用の開発ツール、API、そして高度なランタイムが組み込まれています。zero.data が利用しているのは、この pureQuery の API とランタイムです。pureQuery ツールは Java 成果物を生成するので、zero.data では pureQuery のツールセットの使用を除外していませんが、この記事を書いている時点では、この堅牢なツールセットとの Project Zero 固有の統合はまだありません (pureQuery についての詳細は、「参考文献」を参照してください)。図 1 に、Project Zero の API と pureQuery との関係を示します。
図 1. Project Zero の zero.data リレーショナル・データ・アクセス API 間の関係
zero.data は、pureQuery の API とランタイムが提供する機能をラップするシン・ラッピング API を提供しています。pureQuery の API を使用せずにこのような方法が取られている理由は、Project Zero アプリケーションでの前提を単純化できるようにするため (構成、コネクション・プーリングなど)、さらに共通 API を提供して Groovy スクリプトと PHP スクリプトの両方がそれぞれの言語の理にかなった方法で堅牢な API を使用できるようにするためです。例えば、Groovy インターフェースに公開されるいくつかのメソッドはクロージャーを使用する一方、PHP でそれに対応するものは PHP リソース識別子を使用します。
この記事では、Groovy の API に焦点を当てます。Java および PHP の API について詳しく学ぶには、Project Zero の資料を参照してください。ここから先は、Zero アプリケーションでの基本的な zero.data の使い方について説明します。まず始めに、zero.data.groovy.Manager インターフェースを使用してデータを管理する方法、そしてその設計の背後にある動機を、Java と Groovy 両方の観点から API を検討しながら解説します。続いて簡単な実践例として zero.data API を使用するためのアプリケーションを作成します。このアプリケーションを作成した後、単純なウィキの作成、テーブルの初期化、そして最後にアプリケーションのコーディングを行います。以下に、この記事の概要を示します。
データの管理
zero.data.groovy.Manager (以降、Manager と略します) は、ユーザーがリレーショナル・データベースに対し、クエリーを実行したり、操作を行ったりするのに便利な API を定義しています。この API の最も基本的な使用方法は、SQL ストリングを渡すことです。Groovy では例えば data.queryFirst("SELECT * FROM t WHERE id=$id") といったように、パラメーターをストリングに組み込んで渡します。すると Manager は PreparedStatement を使って SQL のストリングを準備し、その SQL を実行して結果を返します。さらに、作成された最終的なリソース (結果セット、ステートメント、そして接続など) をインテリジェントに管理します。
Manager には、以下の操作を実行する一連のメソッドがあります。
- クエリーを実行し、各行を繰り返し処理してクロージャーを実行する。
- クエリーを実行し、以下のいずれかに変換された ResultSet の先頭行だけを返す。
- Java Bean クラスのインスタンス
- java.util.Map
- クエリーを実行し、以下のいずれかに含まれ、上記のいずれかに変換された ResultSet のすべての行を返す。
- java.util.Iterator
- java.util.List
- Java 配列
- クエリーを実行し、ResultSet を返す。
- 生成されたキー専用のメソッドが組み込まれたデータ操作言語のステートメント (INSERT、UPDATE、DELETE など) を実行する。
単純な API
データの観点からすると、Manager がボイラープレート・コードを減らし、バグが出る可能性のある「サポート」コードではなく機能そのものに専念できるようにする仕組みを比較する価値はあります。リスト 2 では、リスト 1 に記載したコードを JDBC のみで実装しています。これを見ると、Manager がどのようにして単純なことを劇的に簡単にするかがわかります。
リスト 2. JDBC を使用して Map のインスタンスを返す場合の冗長なコード
// assumes Connection has already been initialized
// see JDBC documentation for more details
List<Map<String,Object>> results = new ArrayList<Map<String,Object>>();
try {
PreparedStatement stmt =
connection.prepareStatement("SELECT * FROM person");
ResultSet rs = stmt.executeQuery();
try {
while(rs.next()) {
ResultSetMetaData meta = rs.getMetaData();
int numColumns = meta.getColumnCount();
Map<String,Object> row = new HashMap<String,Object>(numColumns);
for (int i = 1; i <= numColumns; i++) {
row.put(meta.getColumnName(i).toLowerCase(), rs.getObject(i));
}
results.add(row);
}
} finally {
rs.close();
}
} finally {
stmt.close();
}
|
リスト 2 のように JDBC を直接使用するアプリケーション・コードはめったにありません。大抵のフレームワークではある程度の間接化および簡易化が行われていますが、Manager では単純なことを簡単にして難しいことを可能にするためにかなりの取り組みが行われているように思われます。例えばリスト 1 に示されているように、zero.data は Java Bean クラス (Map) の単一のインスタンス (List、Iterator、または Array) を返すだけでなく、開発者、あるいは第三者が複雑な ResultSet の処理を実装することを可能にするテンプレート・メソッドの手法も提供します。
このような一層高度なテンプレート・メソッドの手法を使用する方法については、今後の developerWorks の記事で取り上げることにします。前述したように、zero.data は pureQuery のランタイム・エンジンをベースに作成されています。pureQuery ランタイムはオープン API を使用してデータベース・アクセスを予測可能ながらも柔軟な方法で制約するためのフレームワークになるだけでなく、コードの再利用を大幅に促進します。例えば、pureQuery ランタイム自体のほとんどを構成するインターフェースの実装は、開発者がエンジンを拡張するために実装できるインターフェースそのもののです。このように、pureQuery ランタイムは、特定のアプリケーション用に pureQuery のランタイム・エンジンに「一度限り」の拡張を施したものから、再利用可能な ResultSet 処理コンポーネントのライブラリーを支持する組織に至るまで、あらゆるケースに対応するだけの柔軟性を持ち合わせています。
図 1 に示されているように、Manager は実際に zero.data.Manager をラップして、Groovy から操作しやすいショートカットを提供します。Manager と zero.data.Manager のデータ・アクセス API を比較した以下の表を見てください。この表から、どちらも共通したテーマを持っていることがわかるはずです。つまり、実現する内容はほとんど同じながらも、特殊な使用ケースを目的としたさまざまなメソッド・タイプがあるということです。例えば queryList は java.util.List を返す一方、queryIterator は java.util.Iterator を返します。ほとんど説明するまでもありません。メソッド・タイプのそれぞれにはオーバーロードされたメソッドが 3 つあり、メソッド・タイプのすべてでシグニチャーと関数は対となっています。これらのメソッド・タイプとオーバーロードされたメソッドすべてを、それぞれの説明と併せて表 1 に記載します。詳細は、Project Zero の Javadoc API マニュアルを参照してください。
表 1. Manager メソッドの概要
| メソッド | 説明 | 使用例 | Groovy での使用例 |
|---|
| queryFirst |
ResultSet の最初の行を返す。 | Map<String,Object> queryFirst(String, Object...) | Map<String,Object> queryFirst(GString) |
|---|
| T queryFirst(String, Class<T>, Object...) | T queryFirst(GString, Class<T>) | | T queryFirst(String, RowHandler<T>, Object...) | T queryFirst(GString, RowHandler<T>) | | queryList |
ResultSet を java.util.List として返す。 | List<Map<String,Object>> queryList(String, Object...) | List<Map<String,Object>> queryList(GString) |
|---|
| List<T> queryList(String, Class<T>, Object...) | List<T> queryList(GString, Class<T>) | | List<T> queryList(String, RowHandler<T>, Object...) | List<T> queryList(GString, RowHandler<T>) | | queryIterator |
ResultSet を java.util.Iterator として返す。 | Iterator<Map<String,Object>> queryIterator(String, Object...) | Iterator<Map<String,Object>> queryIterator(GString) |
|---|
| Iterator<T> queryIterator(String, Class<T>, Object...) | Iterator<T> queryIterator(GString, Class<T>) | | Iterator<T> queryIterator(String, RowHandler<T>, Object...) | Iterator<T> queryIterator(GString, RowHandler<T>) | | queryArray |
ResultSet を Java 配列として返す。 | Map<String,Object>[] queryArray(String, Object...) | Map<String,Object>[] queryArray(GString) |
|---|
| T[] queryArray(String, Class<T>, Object...) | T[] queryArray(GString, Class<T>) | | T[] queryArray(String, RowHandler<T>, Object...) | T[] queryArray(GString, RowHandler<T>) | | update |
更新を実行し、更新によって影響を受けた行の数を返す。
| int update(String, Object...) | int update(GString) |
|---|
| insert | 生成したキーの値を 2 番目のパラメーターに指定されたクラスにキャストして返す。Groovy バージョンでは常に int にキャストする。 | T insert(String, Class<T>, String[], Object...) | int insert(GString, List<String>) |
|---|
Java プログラミングと Groovy
表 1 から推測できるように、zero.data にはさまざまな場合に対応する包括的なデータ・アクセス・メソッドのセットがあります。しかしこの記事で使うのは、zero.data.groovy.Manager バージョンの queryFirst(GString)、queryList(GString)、update(GString)、insert(Gstring, List) のみです。
Groovy は Java プラットフォームで実行する動的なオブジェクト指向のプログラミング言語です。Groovy に関してよくある誤解は、Groovy が Java プログラミング言語になり代わろうとしているというものです。Groovy は Java バイト・コードにコンパイルするため、標準 Java API、そして同じく Java 言語で作成された多数のライブラリーを利用することができます。したがって、Groovy は Java 構文の代替手段となることを目的としているのではなく、よりわかりやすい簡潔なコードを可能にする多くの言語要素が含まれる代替構文を提供することによって Java 構文を補うことを目指しています。
例えば Java バージョンの Project Zero の zero.data API を使うだけで、前に記載した標準 JDBC メソッド呼び出しのコードの行数を減らすことができます。ただしリスト 3 を見比べてみると、Java と Groovy のコードは機能的には等価です。
リスト 3. 機能的に等価な Java および Groovy コードの比較
// Java
Manager data = zero.data.Manager.create("wiki");
data.inTransaction(new LocalTransaction() {
public void execute() {
data.update("UPDATE mytable SET name=? WHERE id=?", 1, "new name");
data.insert("INSERT INTO anothertable (name) VALUES (?)", "another name");
List<Map<String,Object>> results = data.queryList("SELECT * FROM sometable");
for (Map<String,Object> row : results) {
for (String key : row.keySet()) {
System.out.println("key: " + key + ", value: " + row.get(key));
}
}
}
});
// Groovy
def data = zero.data.groovy.Manager.create("wiki")
data.inTransaction {
data.update("UPDATE mytable SET name=$name WHERE id=$id")
data.insert("INSERT INTO anothertable (name) VALUES ($name)")
data.eachRow("SELECT * FROM sometable") { row ->
row.each { key, value ->
println("key: $key, value: $value")
}
}
}
|
リスト 3 からわかるように、コードの行数が減るだけでなく、Groovy 言語は余計なことを行わない傾向にあります。この inTransaction メソッドを例に取ると、Java バージョンでは LocalTransaction の匿名インスタンスを明示的に作成しなければなりませんが、Groovy ではクロージャーに隠すことができます。Groovy では、見方によればクロージャーが「移植可能な」コードを作成する手段となります。Java 言語では抽象 LocalTransaction クラスの匿名の実装を作成してメソッド inTransaction に渡すことができるのと同じように、Groovy ではクロージャーの括弧 (「{」と「}」) で囲まれたコードを圧縮して inTransaction メソッドに渡すという遥かに簡潔な方法を取ることができます。Groovy の資料 (「参考文献」を参照) には、クロージャーの例やクロージャーを引数として取る API を作成する方法を含め、Groovy 言語でのクロージャーについて包括的に説明しています。
リスト 3 に記載した Groovy バージョンのコードで使用されている 2 つ目、3 つ目のクロージャーを見てください。eachRow メソッドはクロージャーを取ることが可能で、その場合、結果に含まれる各行に対して括弧内のコードを実行することになります。リストを見るとわかるように、これに該当するコードは別のクロージャーを呼び出し、そのクロージャーが単純に Map 行のエントリーをループしてキーと値を出力します。
アプリケーションの作成
 |
この記事で使用している zero.data API コンパイル時に行う静的な型チェックを実行可能であるが要求はしない動的言語として、Project Zero では Groovy を利用するため、この記事では Map バージョンの API を使用しています。この API では ResultSet の行を java.util.Map にマッピングします。この場合、列名が Map 内のキーとなり、キーの値は ResultSet の行の値となります。 |
|
この記事の残りの部分では、お馴染みのモデル・ビュー・コントローラー・パターンを使用して単純なウィキ・アプリケーションを作成する手順を通して、zero.data の威力を調べていきたいと思います。図 2 に、このパターンが Zero アプリケーションではどのように機能するか大まかなアーキテクチャーを示します。Zero の規約をサポートするコード成果物の配置場所については、記事の最後のほうで説明します。
モデル・ビュー・コントローラー・パターン
図 2 を見てください。この図は、Project Zero アプリケーションでのモデル・ビュー・コントローラーの実装を図解したものです。
図 2. Project Zero アプリケーションでのモデル・ビュー・コントローラー実装
アプリケーションを起動させるには、まず Project Zero アプリケーションを作成する必要があります。以降に記載する図は、Eclipse で Project Zero プラグインを使用した場合の画面です。これらの画面には、アプリケーションを開発するためのユーザー・インターフェースのショートカットが用意されています (ただし、代わりにコマンドライン・ユーティリティーの相当する機能を使ってすべての手順を実行することも可能です)。
最初に作成しなければならないのは Zero アプリケーションです。Project Zero の Eclipse ユーザー・インターフェースを使ってアプリケーションを作成する方法については、「Project Zero の紹介、第 1 回」で詳しく説明しています。さらに、Project Zero アプリケーションを構成するさまざまなフォルダーと成果物についての詳細も記載されているので、ここでは基本的な手順を説明するだけにとどめます。
まず、File > Project.. の順にメニュー項目をクリックしてアプリケーションを作成してください。ダイアログ・ボックスにプロジェクト・ウィザードの選択オプションが表示されるので、Project Zero > Project Zero Application を選択してウィザードを起動します (図 3 を参照)。
図 3. Project Zero Application ウィザードの選択
ウィザードの最初の画面には、アプリケーションの名前 (Project name) を入力するよう求めるプロンプトが表示されます。アプリケーションは任意の名前にできますが、ここではわかりやすいように mywiki という名前を付けることにします (図 4 を参照)。
図 4. 新規 Project Zero アプリケーション名の入力
これで、Eclipse ワークスペース内のプロジェクトとともに、いくつかのデフォルト・アプリケーション成果物が Project Zero アプリケーションのフォルダー構造の中に作成されます。作成された mywiki プロジェクトは図 5 のようになっているはずです。
図 5. Project Zero アプリケーションのフォルダー構造
Project Zero では Ivy フレームワークを使って依存関係を管理するおかげで、とても簡単に zero.data を取得することができます。アプリケーション内で依存関係を宣言する場所は ${app_home}/config/ivy.xml ファイルです。便宜上の理由と単純にすることを目的に、この依存関係の宣言例は組み込みモードの Apache Derby を使って説明します。前述のファイルを編集して、リスト 4 に示す 2 行の XML を組み込んでください。
リスト 4. アプリケーションの ivy.xml への追加内容
<dependency org="zero" name="zero.data" rev="1.0+"/>
<dependency org="org.apache.derby" name="derby" rev="10.3.1.4"/>
|
 |
パスワードの難読化 アプリケーションのプロセスを不足させるデータベースには大抵、接続プロパティーが必須となります。けれども、組み込み Derby をデータベースとして使用している場合には接続プロパティーは不要です。例えば、password プロパティーがないこともあります。Project Zero には、パスワードをわかりにくくして平文で表示されないようにする仕組みがあります。このような構成を実現する方法についての詳細は、Project Zero の資料を参照してください。 |
|
「Project Zero の紹介、第 1 回」の説明に従って新しい依存関係を解決してから、作業を続けてください。
zero.data を構成する
zero.data は javax.sql.DataSource を使ってデータベースに接続します。1 つ、または複数のデータベースを構成して接続するには、Project Zero アプリケーションの zero.config ファイルで接続プロパティーを構成します。リスト 5 では、キー「wiki」を持つデータベースを指定して構成しています。
リスト 5. zero.config での zero.data の構成
/config/db/wiki = {
"class" : "org.apache.derby.jdbc.EmbeddedDataSource",
"databaseName" : "db/wiki",
"connectionAttributes" : "create=true"
}
|
Manager インスタンスを取得する
データベース接続プロパティーを正しく構成したら、今度は Manager インスタンスを取得します。このインスタンスがあれば、前述した便利なメソッドを使ってデータに対するクエリー、操作を実行することができます。構成された Manager を取得するには、zero.config ファイルで選択したキーを使用します。この例では「wiki」を選択したので、これを引数として create メソッドに渡します (リスト 6 を参照)。
リスト 6. 構成済み Manager の取得
def data = zero.data.groovy.Manager.create('wiki')
|
リスト 4、リスト 5、リスト 6 を見るとわかるように、Zero アプリケーションを稼動させるために必要な作業はほんの少ししかありません。次は単純なウィキの作成に取り掛かります。その手順として、基本的な要件を定義してから、それをコードに変換します。
単純なウィキの作成
zero.data の使用例を紹介するため、ここでは単純なウィキ・アプリケーションを作成します。最も単純な形のウィキは、ウィキ・ページの作成、編集、リンク、そしてレンダリングを行います。したがって、この例では標準的な CRUD 操作のうち、作成、取得、更新の操作を取り上げて説明します (削除操作については説明しません)。(訳注: CRUD は、ほとんどの場合、Create、Read、Update、Delete ですが、この記事では Read の代わりに Retrieve が使われています)
なぜウィキなのか
zero.data API を説明するために、なぜウィキを実装するのかというと、第一の理由は単純なウィキはその言葉通り、単純だからです。単純なウィキを実装するのであれば、より複雑なウィキの実装で持ち上がってくるような他の実装詳細を深く掘り下げることなく、zero.data API を中心に説明することができます。第二の理由は、追加機能を後から作成し、さらに高度な zero.data の使い方や Project Zero の他の機能を説明するベースとしてはウィキがあつらえ向きだからです。
 |
ウィキとは何か ウィキとは、ユーザーが素早く簡単に Web ページを作成、編集、リンクできるようにするソフトウェアのことです。この用語は、速い、あるいは形式張らないことを意味するハワイ語に由来しています。 |
|
ウィキの要件
前にも説明したように、基本的なウィキの実装に必要なのは、ウィキ・ページの作成、取得、更新です。ほとんどのウィキの実装に共通している点は、ページの作成は、まず他のページからそのページにリンクしてから行うということです。ウィキのユーザーがリンクをクリックすると、ユーザーにページの作成を促すフォームが表示されます。該当するページがすでに存在する場合には、リンクによってそのページがレンダリングされ、ユーザーはフォームを作成する場合と同じような形でページを編集することができます。
この例では、Project Zero のスクリプト機能とテンプレート作成機能を組み合せ、お馴染みの MVC パターン関連のノウハウを利用して実装をプロトタイプ化します。つまり、アプリケーションの公開フォルダーにコントローラー・スクリプトを配置し、これらのスクリプトには URI によってアクセスできるようにします (PHP アプリケーションの場合とほとんど同様です)。フォルダーの階層とファイル・システム上のファイルは、URI の構造に対応します。一方、ビューはアプリケーションの /app/views フォルダーに保存されます。この実装は Groovy で行うため、コントローラーには接尾部 .groovy、views には接尾部 .gt が付けられます。
どのような結果を目的としているかがよくわかるように、図 6 にアプリケーション成果物を作成した後のアプリケーション・ディレクトリーを示します。
図 6. 手順完了後の Project Zero アプリケーションのフォルダー構造
背景となる情報はこれで十分として、今度はコードの詳細を検討します。
テーブルの初期化
コントローラーとビューの実装に気を取られる前に、データの取得、表示、操作に使用できるモデルを用意しなければなりません。ウィキ・ページはデータベース・テーブルに保管します。単純化を図り、モデルはデータベース・テーブルの行にマッピングされた Map のみとします (このようにしないと、zero.data が主題の記事にならないからです)。ストレージの要件は非常に控えめで、ページの名前と実際のページ・コンテンツ以外に必要なものは何もありません。これを表しているのが、setup.sql ファイルに配置されたリスト 7 の SQL コード・スニペットです。
リスト 7. setup.sql のコンテンツ
CREATE TABLE pages (
id int NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
name varchar(20) NOT NULL,
content varchar(3000),
updated timestamp,
PRIMARY KEY (id)
);
|
起動!
今まで説明した他のアプリケーション成果物とは異なり、setup.sql に関する規約はありません。このファイルに含まれるデータ定義は、起動ハンドラーを使用して実行されます。起動ハンドラーとは、Project Zero アプリケーションに対して提供されるコードで、このコードは (ご想像通り) アプリケーションが起動されるある特定の段階で実行されます。
そこでまず作成するのは、Project Zero アプリケーションの起動時に実行させる Groovy スクリプトです。 データベースがまだ用意されていない場合には、このスクリプトに setup.sql 内のステートメントを実行させます。ここで行おうとしているのは開発時にのみ使える手法です。この手法を実動で繰り返することはできません。現時点では既存のデータベース・スキーマを自動的に変更するためのサポートは存在しないため、アプリケーションのスキーマが変更された場合には起動ハンドラーを使ってデータベースをファイル・システムから削除する必要があります。説明のために、ひとまずベスト・プラクティスから離れますが、この (素朴とまではいかないにせよ) 単純な実装から zero.data API の使用方法を十分理解できるはずです (リスト 8 を参照)。
リスト 8. app/scripts/startup.groovy の内容
import zero.data.groovy.Manager
//if derby database already exists, don't run the script
def db_dir = new File('db/wiki')
if (!db_dir.isAbsolute())
db_dir = new File(config.root[], 'db/wiki')
if (!db_dir.exists()) {
def buffer = new StringBuffer()
new File('setup.sql').text.split('\n').each() { line ->
line = line.trim()
if (line && !line.startsWith("--") && !line.toLowerCase().startsWith("connect"))
buffer << line << '\n'
}
def statements = buffer.toString().split(';')
def data = Manager.create('wiki')
try {
data.inTransaction() {
statements.each() { statement ->
statement = statement.trim()
if (statement)
data.update(statement)
}
}
} catch (Throwable error) {
throw new RuntimeException("Setup database error: ${error.message}", error);
}
}
|
このスクリプトを起動時に実行できるようにするには、スクリプトをそのように構成する必要があります。したがって、config/zero.config ファイルに、このスクリプトを起動ハンドラーとして登録してください (リスト 9 を参照)。
リスト 9. config/zero.config に追加するスタンザ
/config/handlers += {
"events" : "start",
"handler" : "startup.groovy"
}
|
上記の登録では、「start」イベントの起動時にはいかなる場合でも Project Zero ランタイムが startup.groovy スクリプトを実行するように指定しています。これで、Project Zero アプリケーションを起動すると CREATE TABLE 文が実行されるようになります。データベースはまだ存在しないため、組み込み Derby がディスク上にデータベースを作成します (データベースが存在しない場合にはデータベースを作成するように接続プロパティーを構成したからです)。
アプリケーションのコーディング
アプリケーションは実行中のままにしてください。Project Zero は動的なため、ほとんどの変更を反映することができます。テーブルの作成が済んだところで、いよいよコントローラーとビューのコーディングに取り掛かる段階に来ました。まずはページのレンダリングから始めましょう。
動的 HTML のレンダリング
ページをレンダリングするには、Project Zero ランタイムがウィキ・アプリケーションに制御を渡せるようにするためのフックが必要です。そこで、このフックをコントローラーという形で app/public ディレクトリーに実装することにします。このレンダリング機能に付ける名前は「view.groovy」です。まず、レンダリングの際には、Manager にページのクエリーを実行させます。次にクエリーを実行してデータベース内の行を取得し、行が存在する場合はその行をレンダリングし、存在しない場合には作成ページを表示します。この実装は、リスト 10 のとおりです。
リスト 10. view.groovy の実装
def name = request.params.name[]
def data = new zero.data.Manager('wiki')
def page = data.queryFirst("SELECT * FROM pages WHERE name=$name")
if (page) {
request.page.name = page.name
request.page.content = page.content
request.view = 'view.gt'
} else {
request.page.name = name
request.view = 'create.gt'
}
render()
|
次に作成する必要があるのは、view.groovy コントローラーをサポートする「view.gt」および「create.gt」ビューです。この 2 つのビューは、Groovy テンプレートを使って単純に要求スコープ内のデータをレンダリングします (リスト 11 とリスト 12 を参照)。
リスト 11. view.gt の実装
<html>
<head>
<title><%= request.page.name[] %></title>
</head>
<body>
<h1><%= request.page.name[] %></h1>
<%= request.page.content[] %>
<hr/>
<a href="<%= "${getRelativeUri('/edit.groovy')}?page=${request.page.name[]}" %>">
Edit this page?
</a>
</body>
</html>
|
リスト 12. create.gt の実装
<html>
<head>
<title><%= request.page.name[] %> - Create</title>
</head>
<body>
<h1><%= request.page.name[] %></h1>
This page does not exist.
<a href="<%= "${getRelativeUri("/edit.groovy")}?name=${request.page.name[]}" %>">
Create?
</a>
</body>
</html>
|
データベースを更新する
前のセクションでは 2 つのビューを作成しました。1 つはレンダリングされるウィキ・ページ、そしてもう 1 つはウィキ・ページを作成するためのフォームです。これらのビューをサポートするには、追加のコントローラーとビューを作成する必要があります。具体的に言うと、view.gt ビューは edit.groovy コントローラーにリンクし、create.gt は edit.gt をレンダリングすることによって、そのフォームを HTTP POST で save.groovy コントローラーに送信します。edit.groovy はデータを取得し、作成ページと同じようなフォームを表示します。この場合にだけ、フォームには既存のウィキ・ページのコンテンツが設定されます。save.groovy は行を挿入するか、あるいはデータベース・テーブルの既存の行を更新します。このそれぞれの実装を、リスト 13 からリスト 15 に示します。
リスト 13. edit.groovy の実装
def name = request.params.name[]
def data = zero.data.groovy.Manager('wiki')
def page = data.queryFirst("SELECT COUNT(*) FROM pages WHERE name=$name")
if (page) {
request.page.content = page.content
} else {
request.page.content = ""
}
request.page.name = name
request.view = 'edit.gt'
render()
|
リスト 14. edit.gt の実装
<html>
<head>
<title><%= request.page.name[] %> - Editing</title>
</head>
<body>
<h1>Editing <%= request.page.name[] %></h1>
<form method="POST"
action="<%= "${getRelativeUri('/save.groovy')?name=${request.page.name[]}" %>">
<textarea name="content" rows="20" cols="60"><%= request.page.content[] %></textarea>
<input type="submit" value="Save Page"/>
</form>
</body>
</html>
|
リスト 15. save.groovy の実装
def name = request.params.name[]
def data = zero.data.groovy.Manager('wiki')
def page data.queryFirst("SELECT * FROM pages WHERE name=${name}")
def content = request.params.content[]
if (page) {
data.update("UPDATE pages SET content=${content}")
} else {
data.update("INSERT INTO pages (name,content) VALUES ($page_name,$content")
}
uri = "${getAbsoluteUri('/view.groovy')}?name=${name}"
request.headers.out.Location = uri
request.status = HttpURLConnection.HTTP_MOVED_TEMP
|
アプリケーションをテストする
これで、極めて基本的なウィキ・アプリケーションが完成しました。実際、あまりにも基本的なため、アプリケーションをブートするには存在しないウィキ・ページを指定する必要があります。それには、http://localhost:8080/view.groovy?name=HomePage と入力してください。図 7 のページが表示されるはずです。
図 7. HomePage が見つからない場合
上記にはページが存在しないと示されています。これはアプリケーションが機能していることを意味しており、正常な動作です。URI の name パラメーターにはどんな値でも入力できましたが、「HomePage」と入力したのは、これが手始めとしては妥当だと思ったからです。次に、この HomePage のコンテンツを作成します。任意の HTML を入力して、できればページ (存在していないとわかっているページでも構いません) に確実に飛ぶようにリンクを追加してください (図 8 を参照)。
図 8. HomePage の編集
Save Page ボタンをクリックしてページをデータベースに格納すると、上記で編集したページにリダイレクトされてそのページが表示されます (図 9 を参照)。
図 9. HomePage が見つかった場合
存在しないページにリンクを設定した場合 (データベースには現在 1 つのページしか存在しないため、珍しくないケースです)、そのリンクをクリックするとページが見つからないという画面が再度表示されるはずです。その場合には、リンク先のページに対応するページを作成することができます (図 10 を参照)。
図 10. CodingTips が見つからない場合
以上が、この単純なウィキの機能すべてです。このウィキには当然、他にも多くの機能を加えることができます。例えば、Markdown や Textile などのマークアップ・テキストを使えば、特にコンテンツの作成とリンクの指定を単純化することが可能です (後で演習として試してみてください)。
まとめ
Project Zero は、SOA 準拠の Web 2.0 アプリケーションのアジャイル開発を焦点として簡易化された開発プラットフォームです。そんな Project Zero が武器とするライブラリーのなかに、SQL クエリーを実行するための単純な API があります。この記事では、これらの API を利用して単純なウィキを作成する方法を説明しました。
この記事でまず説明したのは、pureQuery API のラッピングを含め、zero.data API の背後にある動機です。さらに zero.data.Manager の詳細を検討した上で、Java バージョンと Groovy バージョンとでの API の違いを概説しました。続いて実際に zero.data API を使用するアプリケーションを作成し、単純なウィキの作成、データベース・テーブルの初期化、そして最後に実装のコーディングを実践してみました。この記事が読者にとって役立ち、これから Zero アプリケーションの作成に取り組もうという気になっていただけたことを望みます。必要とあらば、活発な Project Zero コミュニティーがいつでも助けになってくれるはずです。
ダウンロード | 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|
| Sample Project Zero Eclipse project | wa-pz-wiki.zip | 10KB | HTTP |
|---|
参考文献 学ぶために
製品や技術を入手するために
議論するために
著者について  | 
|  | Brandon Smith はあらゆるデータの責任者として IBM の Project Zero に取り組んでいます。彼は Carnegie Mellon University で修士号を取得しました。連絡先は、16cards.com および brandon+dW@16cards.comです。 |
 | 
|  | Hanumanth Kanthi は、IBM Software Services for WebSphere の IT アーキテクトです。オーストラリアの Victoria University of Technology でコンピューター・サイエンスの修士号を取得しました。連絡先は kanthi@us.ibm.com です。 |
記事の評価
|