 | レベル: 初級 Roland Barcia (barcia@us.ibm.com), Senior Technical Staff Member, IBM Steve Ims ( steveims@us.ibm.com), Senior Technical Staff Member, IBM
2008年 1月 15日 IBM の Project Zero を紹介するこの連載で、Web アプリケーションを作成し、組み立て、そしてデプロイする、Zero ならではの革新的手法を解説付きツアーで体験してください。第 1 回の記事では単純で RESTful なサービスを作成する手順を通して Project Zero を紹介しました。第 2 回目となるこの記事では、引き続き Project Zero が RESTful なソリューションの構築にどのように役立つかを学びます。今回重点とするのは、アプリケーション中心の設計、RESTful なデータのモデル化、REST によるセキュリティー、そして簡易化された RIA および統合です。
RESTful なリソースのモデル化
第 1 回の「Web アプリケーションのための RESTful なサービスを作成する」では、リソース・ハンドラーとデータ API を使って単純なテーブルを RESTful なリソースとして公開する方法を説明しました。また、RESTful なサービスを起動する Dojo ベースのアプリケーションも作成しました。
今回はこのサンプル・アプリケーションを拡張して、より複雑なデータを RESTful なスタイルで公開する方法を紹介します。さらに前回とは別の利用者用アプリケーションも作成します。このアプリケーションの作成手順に従って、Zero を使ってリソースをモデル化し、Zero のイベント・ベースのクライアント・プログラミング・モデルを使って簡単なリッチ・クライアントを作成する方法を説明します。
サンプル・アプリケーションの拡張
第 1 回の演習では、incentive テーブルを RESTful なサービスとして公開しました。今回の記事ではこの一般的なサンプル・アプリケーションを拡張します。図 1 に、ユース・ケース図を示します。
図 1. ユース・ケース
ロールには、利用者 (Consumer) とインセンティブ・プロバイダー (Incentive provider) の 2 つがあります。したがって、次の 2 つのアプリケーションを作成することになります。
- プロバイダー用インセンティブ・アプリケーション
- プロバイダーが、(プロバイダーが提供する) インセンティブを管理し、インセンティブの利用対象者を見つけるための、アプリケーションです。この記事では、インセンティブを対象とした RESTful なサービスの作成のみに焦点を絞ります。
- 利用者用インセンティブ・アプリケーション
- 利用者がインセンティブを見つけられるようにするためのアプリケーションです。プロバイダーまたはロケーションのどちらかを基準にインセンティブを検索できるようにします。
アプリケーション中心の設計とサーバー中心の設計について
エンタープライズでは、サーバー中心の考え方に追従するのは、プラットフォーム (Java™ EE ベースのサーバーなど) です。つまり、Web アプリケーションはアプリケーション・サーバー・プラットフォーム上でビルド、そしてデプロイされ、WebSphere® などのサーバー・プラットフォームが Java EE 仕様で要求されるあらゆるサービス品質を実現することになります。このようなサービスの例には、キュー・ベースのメッセージング、分散トランザクション、プロトコル管理などがあります。アプリケーション・サーバーが 1 つの Java 仮想マシン上で複数のアプリケーションを実行することもよくありました。そして、アーキテクトがアプリケーションを設計する際の基本概念は、ソフトウェアおよびデータ・リソースを他のアプリケーションと共有し、アプリケーション・サーバーからサービスを (そのサービスが使用されていない場合でも) 提供するというものでした。
アプリケーションが、孤立したアプリケーション・サーバーにデプロイされているとしても、大抵の場合は依然としてサーバー自体が利用可能なすべてのサービスを保持します。アプリケーション・サーバーはエンタープライズ・レベルの統合を可能にするからです。エンタープライズ統合の特徴としては、各種システムにまたがる分散トランザクション、重要なデータ配信のためのキュー・ベースのメッセージングなど、さまざまなタイプのサービスがあります。以下の図 2 は、エンタープライズ統合の一例を示したものです。エンタープライズ内のプラットフォームは各種のプロトコルとミドルウェアの管理を中心に設計され、場合によっては多数のアプリケーションに対応するエンタープライズ・データベースと通信します。
図 2. エンタープライズ・レベルの統合
Web 2.0 の世界には、それほど重要でない類の HTTP レベルでの統合が関わってきます。通常、アプリケーション設計の中心となるのは一連のデータです。これらのデータは公開されて他のデータ・セットと組み合わせられ、データ・プロバイダーが予期しないような新しいアプリケーションを作り出すように意図されています。図 3 に、統合の一例を示します。
図 3. Web 2.0 での統合
アプリケーションはデータを中心に設計され、HTTP によって RESTful に公開されます。すると、リッチ・インターネット・アプリケーションは、そのデータをさまざまに組み合わせて適合させることで、新しい状態を作り出すことができます。例えば、マップ・アプリケーションがマップ上のポイントを RESTful に公開すると、雇用管理ツールといったまるで別のアプリケーションが一連の従業員をマップ・アプリケーションに組み合わせ、従業員の配属場所を示すビジュアル・マップを作成することができるといった具合です。こういった機会をもたらすのは、2 つのデータ・セットのマッシュアップです。エンタープライズにおいては重要ではないと言え、このようなアプリケーションがエンタープライズ・プロセスをさらに展開するためには必要になります。
サーバー中心の設計とアプリケーション中心の設計との比較は、スケーリングに関しては行われていません。アプリケーション中心の設計には、アプリケーションのスケーリングを行うための外部エンティティーが必要となり、それにはプロセスを管理し、状態を複製するソフトウェア (WebSphere XD など) が必要になってきます。一方サーバー中心の設計には、アプリケーション・サーバーがスケーリング・メカニズムを組み込むことのできる、より総合的なスケーリング・ソリューションが必要になる場合があります。
アプリケーション中心の設計では、小規模にスタートし、ユーザー数の増加に合わせて規模を拡張していくことは容易であり、ひいては Web 2.0 スタイルの設計における価値命題の実現につながっていきます。そしてもう 1 つの重要な特徴は、Web 2.0 スタイルのアプリケーションからエンタープライズ・アプリケーションを起動できること、またその逆も可能であるという点です。図 4 に示すマッシュアップは、HTTP によってエンタープライズ・アプリケーションにアクセスします。エンタープライズの成果物は WebSphere Web 2.0 Feature Pack などの技術を使用して RESTful に公開できるので、マッシュアップやリッチ・インターネット・アプリケーション (RIA) の一部となることができます。
図 4. マッシュアップによるエンタープライズへのアクセス
いずれの手法にも長所と短所があります。サーバー中心の手法は管理しやすい一方、開発者にとってコードを作成しやすいのはアプリケーション中心の手法です。ただし、アプリケーション中心のソリューションには大抵の場合、それを管理するための外部ソフトウェアが必要になります。
RESTful なデータ
このように、データを Web 2.0 マッシュアップの一部にするにはデータを RESTful に公開することが不可欠です。第 1 回で説明したように Project Zero の中核となるのは REST であり、リソースの公開は REST によって行われます。リソースはインターネットで情報を公開する上での基礎となるので、REST を使用して、異なる Web アプリケーションを容易に構成することができ、複数のリソースからマッシュアップを素早く構成することができます。Project Zero は、このようにリソースを RESTfull に公開するという概念を中心に最適化されています。この記事のサンプル・アプリケーションで公開する必要があるのは、エネルギー・プロバイダーが提供するインセンティブです。図 5 のデータ・モデルに、プロバイダーとそのインセンティブとの関係を示します。
図 5. プロバイダーとインセンティブのデータ・モデル
ここで目標とするのは、関係者がインセンティブを検索できるようにすることです。さらに、プロバイダーがインセンティブを管理する必要もあります。表 1 に、このデータを RESTful に公開する方法を示します (このような表がリソースを RESTful にモデル化する上でいかに役立つかは、第 1 回で説明しました)。
表 1. REST 設計
|
リソース
|
URI
|
メソッド
|
表現形式
|
説明
| | プロバイダー | /provider/<providerId> | GET | JSON オブジェクト | プロバイダーのレコードを取得する | | インセンティブ | /provider/<providerId>/incentive | POST | JSON オブジェクト | インセンティブを新規に作成する | | インセンティブ | /provider/<providerId>/incentive | GET | JSON オブジェクトの配列 | 特定 providerId のインセンティブのリストを取得する | | インセンティブ | /provider/<providerId>/incentive/<incentiveId> | GET | JSON オブジェクト | 個々のインセンティブを取得する | | インセンティブ | /provider/<providerId>/incentive/<incentiveId> | PUT | JSON オブジェクト | 単一のインセンティブを更新する | | インセンティブ | /provider/<providerId>/incentive/<incentiveId> | DELETE | | 単一のインセンティブを削除する | | インセンティブ | /incentive?location=<state_name> | GET | JSON オブジェクトの配列 | 任意のプロバイダーの特定状態にあるインセンティブのリストを返す |
第 1 回とは異なり、インセンティブにはプロバイダーの名前空間でアクセスすることができます。インセンティブのライフサイクルはプロバイダーに結び付いているため、提供元のプロバイダーがこれを管理します。RESTful な方法により、インセンティブ・レコードが確実に該当プロバイダー内でネストされ、要件を満たすようにすることはできますが、利用者がプロバイダー全体でインセンティブを検索したい可能性もあります。その場合には、/incentive 名前空間を使用すると、複数のプロバイダーを範囲としたインセンティブのリストを表示することができます。
プロバイダーの数が多い場合には、インセンティブの名前空間がかなり大きくなる可能性があるため、何らかの方法で名前空間を制限しなければなりません。このサンプル・アプリケーションでは、ロケーションを指定しないと /incentive 名前空間にアクセスできないようにしています。また、選択範囲を絞り込むためにクエリー・パラメーターも使用することにしました。
RESTful なサービスに適用する非機能要件
サービスを定義したら、次はサービス品質をサービスに加えられるようになります。非機能要件は、形もサイズもさまざまです (非機能要件に対する適切で実際的な対処法については、記事「Why do non-functional requirements matter?」を参照してください)。REST では HTTP ベースのサービスを提供するため、非機能要件の適用は簡易化することができます。このサンプル・アプリケーションではセキュリティーに対処するため、URL をセキュアにすることによって REST リソースにセキュリティー・ルールを適用します。表 2 は、セキュリティーに重点を置いた REST の例を示す表です。
表 2. セキュアな REST リソース
|
リソース
|
URI
|
メソッド
|
ロール
|
インスタンス・レベルでのセキュリティーの必要性
| | プロバイダー | /provider/<providerId> | GET | すべて | なし | | インセンティブ | /provider/<providerId>/incentive | POST | プロバイダー | あり。各プロバイダーが管理できるのは所有するインセンティブのみ | | インセンティブ | /provider/<providerId>/incentive | GET | すべて | なし | | インセンティブ | /provider/<providerId>/incentive/<incentiveId> | GET | すべて | なし | | インセンティブ | /provider/<providerId>/incentive/<incentiveId> | PUT | プロバイダー | あり。各プロバイダーが管理できるのは所有するインセンティブのみ | | インセンティブ | /provider/<providerId>/incentive/<incentiveId> | DELETE | プロバイダー | あり。各プロバイダーが管理できるのは所有するインセンティブのみ | | インセンティブ | /incentive?location=<state_name> | GET | すべて | なし | | その他のリソース | /<Anything Else> | すべて | 該当者のみ | 適用外 |
表 2 に示しているのは、URL リソースとそのリソースにアクセスすることができるロールです。重要な考慮事項として、REST はエンティティーの公開パターンを定義します。データによってはインスタンス・ベースのセキュリティーが必要なことはよくあります (例えば、その所有者だけがデータを表示できるようにするなど)。このサンプル・アプリケーションの場合、インセンティブを作成、更新、削除できるのは、そのインセンティブを所有するプロバイダーだけに制限します。この制限をプロバイダーのロールの一部とするだけでは不十分なので、上記の設計表にはデータをインスタンス別に保護する必要があるかどうかを示す列を設けています。
サンプルを実行するための前提条件
この記事のサンプルを実行するには、以下が必要です。
-
Eclipse 3.2 以降。この記事では Eclipse 3.3 を使用しました。
- Eclipse 用 Project Zero Java および Groovy プラグイン
このサンプルは、Zero の M3 Release で作成されています。したがって、Eclipse Update Site は必ず http://www.projectzero.org/update/zero.eclipse.M3 に設定してください。
PHP プラグインはこの演習には必要ありません。プラグインのインストールに関する情報は Project Zero Downloads を参照してください。
注: 第 1 回で M1 プラグインをインストールした場合は、これをアンインストールしてから M3 フィーチャーおよびプラグインをインストールする必要があります。プラグインのアンインストール方法については Eclipse のヘルプを参照してください。
- この記事に付属のダウンロード・ファイル。このファイルは C: ドライブに解凍してください。
-
Firefox ブラウザー
- 連載第 1 回で説明した概念を十分に理解していること
その他、以下のツールが役に立ちます。
RESTful なプロバイダー用アプリケーションを作成する
まず始めにプロバイダー用アプリケーションを作成します。目標は、前に定義した RESTful なサービスを作成することです。第 1 回で学んだ知識を基に、プロバイダー用インセンティブ・アプリケーションを作成していきます。
アプリケーションの新規作成
以下の手順に従って、新規アプリケーションを作成します。
- 新しいワークスペースを開きます。
図 6.
- 新規プロジェクトを作成します。
図 7.
-
Project Zero -> Project Zero Application をプロジェクト・タイプとして選択します。
図 8.
- アプリケーションに
ProviderIncentiveApp という名前を付けます。
図 9.
依存関係の追加
必要な依存関係を追加する必要があります。第 1 回で説明したように、依存関係の管理には ivy 技術を使用しました (詳細は、第 1 回の記事、または Project Zero Developer’s Guide を参照してください)。
- config ディレクトリーの下にある ivy.xml ファイルを開きます。
図 10.
- Dependencies セクションで Add をクリックします。
図 11.
- 以下の依存関係を追加します。
- zero.data
- zero.data.setup.webtools
- derby
以下に示すのは、依存関係ウィザードです。
図 12.
- ivy.xml ファイルを保存します。ソースを調べると、リスト 1 のような内容になっているはずです。
リスト 1
<!-- Note: dependencies from maven require the maven2 notation. -->
<dependencies>
<dependency name="zero.core" org="zero" rev="1.0+"/>
<dependency name="zero.webtools" org="zero" rev="1.0+"/>
<dependency name="zero.data" org="zero" rev="1+"/>
<dependency name="zero.data.setup.webtools" org="zero" rev="1+"/>
<dependency name="derby" org="org.apache.derby" rev="10+"/>
</dependencies>
|
-
Update Dependencies アイコンをクリックします。以前も説明したように、このアイコンをクリックするとリモート・リポジトリーとローカル・リポジトリーにある、すべての構成済みパッケージの最新バージョンの確認が行われます。
図 13.
-
OK をクリックして更新を確認します。
図 14.
- 以上の作業が終わったら、エディターを閉じます。
テーブルの作成
アプリケーションのコーディングには組み込みバージョンの derby を使用します。開発の際には、これがふさわしい選択肢です。サンプルに必要なテーブルの作成には、Project Zero の M3 ドライバーに新しく導入されたデータベース・セットアップ・ツールを使います。
-
ProviderIncentiveApp プロジェクトを右クリックします。
図 15.
-
General->File System を選択します。
図 16.
-
C:\ProjectZeroArticleSeries/Part2Artifacts/ProviderAppArtifacts を選択します (ダウンロード・ファイルを C: ディレクトリーに解凍したという前提です)。sql を選択してから Finish をクリックします。
図 17.
- プロジェクト・ディレクトリーのレイアウトは、以下の図のようになります。
図 18.
- create.sql ファイルを開きます。ここに、PROVIDER および INCENTIVE テーブルを作成するための DDL が含まれています (リスト 2 を参照)。
リスト 2
CREATE TABLE PROVIDER (
PROVIDER_ID VARCHAR (50) NOT NULL,
NAME VARCHAR(256) NOT NULL,
DESCRIPTION VARCHAR(256) NOT NULL,
LOCATION VARCHAR(50) NOT NULL,
PROVIDER_TYPE VARCHAR(128),
CONTACT VARCHAR(256));
ALTER TABLE PROVIDER ADD CONSTRAINT PROVIDER_PK PRIMARY KEY (PROVIDER_ID);
CREATE TABLE INCENTIVE (
INCENTIVEID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY(START WITH 1, INCREMENT BY 1),
NAME VARCHAR(256) NOT NULL,
DESCRIPTION VARCHAR(256) NOT NULL,
PROVIDER_ID VARCHAR (50) NOT NULL,
INCENTIVETYPE VARCHAR(128),
VALIDFROM TIMESTAMP,
VALIDTO TIMESTAMP,
WEBSITE VARCHAR(256)
);
ALTER TABLE INCENTIVE ADD CONSTRAINT INCENTIVE_PK PRIMARY KEY (INCENTIVEID);
ALTER TABLE INCENTIVE ADD CONSTRAINT INC_PPR_FK FOREIGN KEY (PROVIDER_ID)
REFERENCES PROVIDER (PROVIDER_ID);
|
- このように、テーブルを削除してサンプル・データを追加するためのファイルが 1 つあります。次に、アプリケーションを実行するため、プロジェクトを右クリックして Run As -> Project Zero Application を選択します。
図 19.
- コンソールで、アプリケーションが起動して実行中になっていることを確認します。
図 20.
- ブラウザーを開いて、http://localhost:8080/setup にアクセスします。ブラウザーの表示は以下の図のようになるはずです。
図 21.
- 以下の情報を入力した上で、Create Tables をクリックします。
- Database Name:
PRO_DB
- Database Type: Apache Derby (Embedded)
- User Name:
APP
図 22.
create.sql スクリプトが正常に実行されたというメッセージが表示されるはずです。
-
Add Sample Data をクリックします。sample.sql スクリプトが正常に実行されたというメッセージが表示されるはずです。
図 23.
- コンソールで Stop ボタンをクリックしてアプリケーションを停止します。
開発の場合には、このようにアプリケーションを停止、起動しても問題ありませんが、テスト環境や実動環境では管理コマンド・ラインを使用してアプリケーションの停止と起動を行うことをお勧めします。詳細は、Zero Management CLI extensions reference を参照してください。
図 24.
プロバイダー用インセンティブ・アプリケーションのための RESTful なサービスの作成
プロバイダー用インセンティブ・アプリケーションのための RESTful なサービスを作成するには、以下の手順に従ってください。
-
app/resources フォルダーを右クリックして New -> File を選択します。
図 25.
- 新しいファイルに
provider.groovy という名前を付けます。
図 26.
-
Yes をクリックして Groovy サポートをプロジェクトに追加します。
図 27.
- provider.groovy ファイルが開いていることを確認して、リスト 3 のコードを入力します (または、C:\ProjectZeroArticleSeries\Part2Artifacts\ProviderAppArtifacts\codeSnippet\proSnippet.1.txt からこのコードを貼り付けることもできます)。
このコードはデータ API を使用してクエリーを実行し、その結果を JSON 形式で格納します。このパターンは連載第 1 回に記載したとおりです。API についての詳細は、Project Zero Developer’s Guide のデータ・アクセスに関するセクションを参照してください。
onRetrieve メソッドは、プロバイダーの単一インスタンスを取得するハンドラーを表すために使用します。このパターンも同じく第 1 回に記載されています (または、Project Zero Developer’s Guide の Part 1 (or, see Resource handling を参照してください)。
サービスの上には、アノテーション付きの情報を伴ったコメントがあります。Project Zero では、このようにして RESTful なリソースを文書化しますす。Project Zero はアノテーション付きコメントを使用して、RESTful な文書、そして REST サービスのテスト・ツールをレンダリングすることになります。このツールの動作は後で紹介します (詳細は、RESTful documentation を参照)。以下のサンプルでは、利用可能な HTTP リターン・コードを文書化し、ファイル形式を記述するとともに戻り値の表示例を記載しています。
リスト 3
import zero.data.groovy.Manager;
/**
*
* @success 200 Returns the profile for the provider with the given ID.
* @error 404 Not authorized Provider for User
* @format application/json
* @example
* {
* "name": "Energy Provider Name",
* "description": "Sample Output",
* "location": "New Jersey"
* "provider_type": "energy"
* "contact": "roland@projectzero.org"
* }
*
*/
def onRetrieve()
{
def data = Manager.create('PRO_DB');
def result = null;
def id = request.params.providerId[0];
//Note: Next piece of code should be in one line.
result =
data.queryFirst
("select name,description,location,provider_type,contact from provider where
provider_id = $id");
if(result != null) {
request.view='JSON'
request.json.output = result
render()
} else {
request.status = HttpURLConnection.HTTP_NOT_FOUND
request.error.message = "Provider $id not found."
request.view = "error"
render()
}
}
|
- このデータベース・ツールを使うと、データベースの構成が data.config ファイル内に生成されます。アプリケーションがこの構成を使用できるようにするには、構成をマスター zero.config ファイルに組み込む必要があります (zero 構成モデルについては第 1 回で説明していますが、zero の構成に関してさらに詳しい内容を調べるには Configuration を参照してください)。
図 28.
- zero.config ファイルの最初の部分を見てください。単純化された新しい構成用の構文があることに気付くはずです。
図 29.
- 以下の図に示す 2 番目のエントリーには、生成されたデータ構成が記載されています。複雑なプロパティーのための新しい構文は JSON 形式を使用します。
図 30.
- メニュー・バーの起動ボタンを使ってアプリケーションを起動します。
図 31.
- ブラウザーから http://localhost:8080/resources/docs にアクセスします。以下の図に示されているのは、使用可能な RESTful なリソースです。Provider を選択してください。
図 32.
- リソース文書化ツールが groovy ファイル内のコメントされた情報をレンダリングします。REST ツールを使ってサービスをテストすることもできます。
図 33.
- フォーマットをクリックすると (以下を参照)、サンプルを表示することができます。
図 34.
図 35.
- この RESTful なサービス用のテスト・ドライバーを起動するには、以下に示す URI をクリックします。
図 36.
-
Send をクリックします。
図 37.
- JSON オブジェクトが返されたことが結果に示されます。
図 38.
インセンティブ・サービスの作成
このセクションではインセンティブ・サービスを作成し、そのインセンティブ・サービスをプロバイダー・サービスに関連付けます。
- /app/resources フォルダーに新しいファイルを作成し、
incentive.groovy という名前を付けます。
図 39.
- /app/resources フォルダーにもう 1 つ新しいファイルを作成し、
incentive.bnd という名前を付けます。
図 40.
- 以下の 2 行を入力します。
provider/incentive
incentive |
上記はプロバイダーとインセンティブとの関係を定義する行です。ここでは、/provider/<provider_id>/incentive と /incentive 名前空間の両方をサポートするように指定しています。ネストされたパターンだけを許可する場合は、provider/incentive のみを指定することになります。
図 41.
- 以下の
onList メソッドを追加します (または、C:\ProjectZeroArticleSeries\Part2Artifacts\ProviderAppArtifacts\codeSnippet/proSnippet3.txt からこのメソッドを貼り付けてください)。ここで注目する点は、groovy ショートカットを使用してヘッダーを調べ、まずプロバイダーが存在するかどうかを確認していることです。プロバイダーが存在する場合は、そのプロバイダーのすべてのインセンティブを対象にクエリーを実行します。providerId が存在しない場合には、リクエストの対象は /incentives 名前空間となります。また、location パラメーターの有無もチェックされます。このパラメーターがある場合には、クエリーによる SQL 検索が実行され、ない場合には他の呼び出しは許可されません。
リスト 4
import zero.data.groovy.Manager;
/**
*
* @success 200 Returns incentives by provider or location.
* @error 404 Cannot Query all Incentives in the system.
/provider/providerId/incentive or /incentive?location=locationValue
must be used.
* @format application/json
* @example
* {
* "incentiveId" : "Incentive Id"
* "name": "Incentive Name",
* "description": "Sample Output",
* "providerName": "Provider Name"
* "providerId": "Provider Id"
* "incentive_type": "Incentive Type"
* "validfrom": 1189396800000,
* "validto": 1189396800000,
* "website": "http://www.projectzero.org"
* }
*
*/
def onList()
{
def data = Manager.create('PRO_DB');
def provider = null
def location = null;
// List by provider flag
if(request.params.providerId)
provider = request.params.providerId[0];
// List by location flag
if(request.params.location)
location = request.params.location[0];
def result = null;
if(provider != null)
{
result = data.queryList("select i.incentiveId,i.name,p.name
as providerName,i.provider_id,i.incentivetype as
incentive_type,i.validFrom,i.validTo,i.website,p.location from provider
as p,incentive as i where p.provider_id = $provider and i.provider_id =
p.provider_id");
}
else if (location != null)
{
result = data.queryList("select i.incentiveId,i.name,p.name
as providerName,i.provider_id,i.incentivetype as
incentive_type,i.validFrom,i.validTo,i.website,p.location from provider
as p,incentive as i where p.location = $location and i.provider_id =
p.provider_id");
}
else
{
request.status = HttpURLConnection.HTTP_NOT_FOUND
request.error.message = "Cannot Query all Incentives in the
system. /provider/providerId/incentive or
/incentive?location=locationValue must be used."
request.view = "error"
render()
return;
}
if(result != null) {
request.view='JSON'
request.json.output = result
render()
}
}
|
- ファイルを保存します。
ブラウザーに戻り、http://localhost:8080/resources/doc にアクセスして Incentive リンクを選択します。
図 42.
- インセンティブ URI はプロバイダー名前空間の下にあります。
M3 にはバグがあり、Resource 文書にはそのリソースの 1 つのパターンしか表示されません。このバグは次回のマイルストーンで修正される予定です。
図 43.
- URI をクリックします。Provider ID には
austinEnergy と入力してください。
図 44.
- Austin Energy のインセンティブ・リストが再表示されます。
図 45.
- ブラウザーで、http://localhost:8080/resources/incentive のインセンティブを検索してみてください。以下に示すようなエラーが返ってくるはずです。
図 46.
- ブラウザーか、または Firefox Poster のようなツールを使って、http://localhost:8080/resources/incentive?location=Texas の GET リクエストを実行します。以下の図は、Poster ツールを使用した場合の結果です。
図 47.
- incentive.groovy ファイルに戻って、このファイルにリスト 5 のコードを入力します (または、C:\ProjectZeroArticleSeries\Part2Artifacts\ProviderAppArtifacts\codeSnippet/proSnippet3.txt からコードを貼り付けます)。このコードには、個々のインセンティブを取得するための呼び出し、そしてインセンティブを作成、更新、削除するための呼び出しが含まれています。このコードは、第 1 回に記載したタイプのコードとよく似たものです。
リスト 5
/**
*
* @success 200 Returns incentive by id.
* @error 404 Incentive Id not found.
* @format application/json
* @example
* {
* "incentiveId" : "Incentive Id"
* "name": "Incentive Name",
* "description": "Sample Output",
* "providerName": "Provider Name"
* "incentive_type": "Incentive Type"
* "validfrom": 1189396800000,
* "validto": 1189396800000,
* "website": "http://www.projectzero.org"
* }
*
*/
def onRetrieve()
{
def data = Manager.create('PRO_DB');
def id = request.params.incentiveId[0];
result = data.queryFirst("select
i.incentiveId,i.name,i.description,p.name as
providerName,i.incentivetype as
incentive_type,i.validFrom,i.validTo,i.website from provider as
p,incentive as i where i.incentiveId = $id");
if(result != null) {
request.view='JSON'
request.json.output = result
render()
} else {
request.status = HttpURLConnection.HTTP_NOT_FOUND
request.error.message = "Incentive $id not found."
request.view = "error"
render()
}
}
/**
*
* @success 204 Post new Incentive for the provider.
* @error 403 Not authorized to insert this record.
* @error 404 Cannot Post directly to /incentives, only
/provider/<providerId>/incentive
* @format application/json
* @example
* {
* "name": "Incentive Name",
* "description": "Sample Output",
* "incentive_type": "Incentive Type",
* "validfrom": 1189396800000,
* "validto": 1189396800000,
* "website": "http://www.projectzero.org"
* }
*
*/
def onCreate()
{
def provider = request.params.providerId[0];
if(provider != null)
{
def data = Manager.create('PRO_DB');
def incentive = zero.json.JsonType.fromData(request.input[]).getJson()
def user = request.subject['remoteUser'];
def validFrom = new java.sql.Timestamp(incentive.validfrom);
def validTo = new java.sql.Timestamp(incentive.validto);
def key = data.insert("insert into incentive
(name,description,provider_id,incentivetype,validfrom,validto,website)
values ($incentive.name,$incentive.description,$provider,$incentive.incentivetype,
$validFrom,$validTo,$incentive.website)",['incentiveId']);
def locationUri = getRequestedUri(false) + '/' + key
request.headers.out.Location = locationUri
request.status = HttpURLConnection.HTTP_NO_CONTENT
}
else
{
request.status = HttpURLConnection.HTTP_NOT_FOUND
request.error.message = "Cannot POST directly to /incentive,
only /provider/<providerId>/incentive."
request.view = "error"
render()
return;
}
}
/**
*
* @success 204 Delete Incentive for the provider.
* @error 403 Not authorized to insert this record.
* @error 404 Cannot delete directly to /incentives,
only /provider/<providerId>/incentive
*
*/
def onDelete()
{
def provider = request.params.providerId[0];
if(provider != null)
{
def data = Manager.create('PRO_DB');
def id = request.params.incentiveId[0];
def user = request.subject['remoteUser'];
data.update("delete from incentive where incentiveId = $id");
request.status = HttpURLConnection.HTTP_NO_CONTENT
}
else
{
request.status = HttpURLConnection.HTTP_NOT_FOUND
request.error.message = "Cannot DELETE directly to
/incentive, only /provider/<providerId>/incentive."
request.view = "error"
render()
return;
}
}
/**
*
* @success 204 Incentive updated for provider.
* @error 403 Not authorized to update this record.
* @error 404 Cannot Put directly to /incentive/<incentiveId>,
only /provider/<providerId>/incentive/<incentiveId>
* @format application/json
* @example
* @example
* {
* "name": "Incentive Name",
* "description": "Sample Output",
* "incentive_type": "Incentive Type",
* "validfrom": 1189396800000,
* "validto": 1189396800000,
* "website": "http://www.projectzero.org"
*
* }
*
*/
def onUpdate()
{
def provider = request.params.providerId[0];
if(provider != null)
{
def data = Manager.create('PRO_DB');
def incentive = zero.json.JsonType.fromData(request.input[]).getJson()
def user = request.subject['remoteUser'];
def id = request.params.incentiveId[0];
def validFrom = new java.sql.Timestamp(incentive.validfrom);
def validTo = new java.sql.Timestamp(incentive.validto);
data.update("update incentive set
name=$incentive.name,description=$incentive.description,incentivetype =
$incentive.incentiveType,validfrom=$validFrom,validto=$validTo,website=
$incentive.website where incentiveId = $id ");
request.status = HttpURLConnection.HTTP_NO_CONTENT
}
else
{
request.status = HttpURLConnection.HTTP_NOT_FOUND
request.error.message = "Cannot Put directly to
/incentive/<incentiveId>, only
/provider/<providerId>/incentive/<incentiveId>."
request.view = "error"
render()
return;
}
}
|
操作のセキュリティー保護
要件の一環として、インセンティブの作成、更新、削除の操作はそのインセンティブを所有するプロバイダーだけが行えるようセキュアにしなければなりません。この作業は、まず組み込み Zero File Registry を使って始めます。User File Registry は開発に適したオプションです。Project Zero は LDAP ベースのレジストリーもサポートしており、大抵の実動環境では LDAP を使用することがベスト・プラクティスです。Project Zero では、デプロイメント時にデフォルト・レジストリーを上書きするのは簡単です。
- ブラウザーで http://localhost:8080/zero/webtools/user にアクセスします (以下の図を参照)。
以下の情報を入力します。
- UserName: austinEnergy
- Password: passw0rd
- Groups: Provider
add をクリックします。
図 48.
- 別のユーザーを以下の図のように入力します。
- UserName: centerPoint
- Password: passw0rd
- Group: Provider
add をクリックします。
図 49.
- これで、File Registry に 2 人のユーザーが登録されたことになります。
図 50.
- config ディレクトリーには、以下に示すように zero.users ファイルが表示されているはずです (このファイルを表示するには Eclipse Project を更新しなければならない場合があります)。
図 51.
許可ルールの追加
この時点で、許可ルールを追加する必要があります。RESTful な URI はセキュアにしなければなりません。サポートされない URI は存在しないグループでセキュアにし、さらに RESTful なデータは所有者だけがアクセスできるようにします。これは、Project Zero がインスタンス・ベースのセキュリティーをサポートしていることを示しています。
- config ディレクトリーに新しいファイルを作成します。
図 52.
- 新しいファイルに
security.config という名前を付けます。
図 53.
-
リスト 6 のテキストを入力します (または、C:\ProjectZeroArticleSeries\Part2Artifacts\ProviderAppArtifacts\codeSnippet\proSnippet4.txt からこのテキストを貼り付けます)。
このテキストは rule.config を組み込み、一連のパラメーターを入力します。uri は REST url を定義するパラメーターです。垂直バーは、URI パターンの後に続くあらゆるものも考慮します。最初のルールでは、/incentive 名前空間での UPDATE HTTP メソッドを誰に対しても非許可とし、2 番目のルールではログインしたプロバイダーだけに UPDATE メソッドの実行を許可しています。例えば austinEnergy としてログインした場合に austinEnergy 名前空間で実行できる操作は、POST、DELETE、PUT のみです。詳細は、Project Zero Developer’s Guide の Security considerations に記載されています。
リスト 6
@include "${/config/dependencies/zero.core}/config/security/rule.config"
{
"uri":"/resources/incentive|",
"condition":"urimatches",
"authType":"Basic",
"groups":"[NO_ONE]",
"methods":"DELETE|POST|PUT"
}
@include "${/config/dependencies/zero.core}/config/security/rule.config"
{
"uri":"/resources/provider/{remoteUser}|",
"condition":"urimatches",
"authType":"Basic",
"methods":"DELETE|POST|PUT"
}
|
- config ディレクトリーに含まれている zero.config ファイルを開きます。
図 54.
- security.config ファイルのための
include エントリーを追加します。
図 55.
セキュリティーのテスト
RESTful なリソースをセキュアにしたところで、REST 文書化ツールを使ってセキュリティーのテストを行います。
- プロバイダー用アプリケーションを終了 (コンソールの Stop ボタンをクリック) してから再起動します。起動ショートカットを使用しても構いません。
図 56.
- ブラウザーで http://localhost:8080/resources/docs にアクセスし、Incentive をクリックします。
図 57.
- 使用できる HTTP のメソッドがすべて表示されます (以下を参照)。
図 58.
- 個々のレコードを対象とした GET をクリックします。
図 59.
- Provider ID には
austinEnergy を、Incentive ID には 1 を入力します。
図 60.
- JSON オブジェクトが返されます。
図 61.
- 結果ウィンドウを閉じます。
POST の形式をクリックします。
図 62.
- JSON のサンプルをコピーします。
図 63.
- POST の URI をクリックします。上記の値を Body に貼り付け、Provider ID には
austinEnergy と入力します。
図 64.
- 認証は成功するはずです。
User Name には austinEnergy、Password には passw0rd と入力します。
図 65.
- 以下の結果が表示されます。POST の結果が格納されている場所をメモしておいてください。
図 66.
- 必要であれば、Incentive ID を 5 に設定して PUT と DELETE をテストできます。以下の図では、DELETE の場合を示しています。
図 67.
- austinEnergy としてログインしている状態で、URI を使用して POST を再び開きます。
centerPoint URI にあるデータで POST を試してみてください。
図 68.
- 403 Forbidden エラーが返ってくるはずです。
図 69.
アプリケーションのインセンティブに関する演習はこれで完了です。次は利用者用サンプル・アプリケーションを作成するので、アプリケーションは実行中のままにしてください。
利用者用アプリケーションを作成する
 | |
次回の記事では、利用者用アプリケーションと RESTful なデータについて掘り下げて説明します。
|
|
これまで Data API を使ってリソース・ハンドラーを作成する方法を説明してきましたが、Project Zero の M2 にはリソースをモデル化する別の方法も導入されています。SQL のコーディングを選ぶか、パーシスタンス技術を選ぶかは開発者によってさまざまです。Zero Resource モデルでは、データと REST (オブジェクトとは限りません) とのマッピングに重点が置かれています。
リモート REST リソースを呼び出すために Zero Connection API を使用する高速クライアントを作成するには、Zero Resource に加え、Zero Web Template 技術を使用することになります。
アプリケーションの新規作成
最初のステップは、新しいアプリケーションを作成することです。
-
ConsumerIncentiveApp という名前の Project Zero Application を新規に作成します。
図 70.
-
ConsumerIncentive アプリケーションを右クリックして Import を選択します。
図 71.
- From directory は C:\ProjectZeroArticleSeries\Part2Artifacts\ConsumerAppArtifacts に設定されているはずなので、sql フォルダーのみを選択します (以下を参照)。
図 72.
- 以下の図に示す create.sql ファイルを確認します。
図 73.
- consumer テーブルはフィールドが 4 つの非常に単純なものであることを確認してください。
図 74.
-
zero.config ファイルを開きます。
図 75.
- デフォルトのポート番号を
8081 に変更します。これにより、両方のアプリケーションを同時に実行できるようになります。
図 76.
-
ivy.xml を開きます。
図 77.
- Dependencies に以下の依存関係を追加します。
- derby
- dojo
- zero.data
- zero.data.setup.webtools
- zero.web.template
- zero.resource
図 78.
- 依存関係を更新します。
図 79.
- Consumer を実行します。
図 80.
- Console タブ上で、アプリケーションが Port 8081 で実行中になっていることを確認します。
図 81.
- 利用者用アプリケーションに対してデータベース・セットアップ・ツールを実行します。ポートは http://localhost:8081/setup/ のように指定します。
Database Name には CON_DB、Database Type には Apache Derby (Embedded) と入力し、Create Tables をクリックします。
図 82.
-
Add Sample Data をクリックします。
図 83.
- アプリケーションの zero.config ファイルをもう一度開き、/config/reosurce/dbKey="COB_DB" という行を図 84 のように入力します。
図 84.
- 利用者用アプリケーションを終了します (プロバイダー用インセンティブ・アプリケーションは終了しないでください)。
単純なリソース・モデルの作成
このセクションでは、consumerテーブルに基づく単純なリソース・モデルを作成します。M2 はこの技術のプレビュー・リリースなので、その使用方法を表すには極めて単純なパターンを使います (この連載では今後、これよりも手の込んだサンプルを使用する予定です)。
- 新しいソース・フォルダーを作成するために、
app ディレクトリーを右クリックして New -> Source フォルダーを選択します。
図 85.
-
app/models という名前のフォルダーを作成します。
図 86.
- app/models フォルダーのなかに新規ファイル、
consumer.groovy を作成します。
図 87.
-
Finish をクリックして Groovy サポートを追加します。
- リスト 8 のコードを consumer.groovy に入力します (または、C:\ProjectZeroArticleSeries\Part2Artifacts\ConsumerAppArtifacts\conSnippet2.txt からこのコードを貼り付けます)。これによって、単純なモデルが作成されます。
リスト 8
import zero.resource.fields.*
fields = [
name: [type:'CharField'],
state:[type:'CharField'] ,
provider:[type:'CharField']
]
|
- これで、Zero API または HTTP を使用してローカルからデータにアクセスできるようになります。
HTTP でモデルを公開するために、/app/resourcesフォルダーにファイルをもう 1 つ作成します。
図 88.
- このファイルにも consumer.groovy という名前を付けます。consumer.groovy ファイルの重複に関するエラーが表示されますが、このエラーは無視して構いません。
図 89.
- 以下に示すように、
ZRM.delegate() と入力します。このコードは、HTTP によってモデルを公開します。
図 90.
- 利用者用アプリケーション (プロバイダー用アプリケーションではありません) が実行されている場合は終了してから、再度、利用者用アプリケーションを起動します。
図 91.
- ブラウザーを開いて (または Firefox Poster プラグインを使用して)、http://localhost:8081/resources/consumer にアクセスします。
図 92.
- 最終的な JSON テキストを開き、利用者のリストを表示します。
図 93.
-
http://localhost:8081/resources/consumer/
1
にアクセスします。取得されるレコードは 1 つだけのはずです。
図 94.
- 単一のレコードが返されます。
図 95.
単純なクライアントの作成
このセクションでは、Zero の Web テンプレートを使って非常に単純なリッチ・インターネット・クライアントを作成します。第 1 回では Dojo クライアントから RESTful なサービスを起動する方法を説明しましたが、Zero ではこの他のパターンもサポートします。例えば、ここで説明する HTML フラグメントをフェッチするというパターンです (この連載では今後、Dojo と Zero Templating を併せて使用する予定です)。Zero クライアントについての詳細は、Writing rich Web applications を参照してください。
Zero クライアント・プログラミング・モデルでは、Zero サーバー・イベントが JavaScript イベントに応答することが可能になります。Zero サーバー・イベントは、グローバル・コンテキストのクライアント・ゾーンを使ってデータを共有することもできます。
-
public ディレクトリーの下に新しいファイルを作成します。
図 96.
- ファイルに
incentiveSearch.zhtml という名前を付けます。
図 97.
-
リスト 9 の HTML コードを入力します (または、C:\ProjectZeroArticleSeries\Part2Artifacts\ConsumerAppArtifacts\conSnippet3.txt からこのコードを貼り付けます)。
この HTML テンプレートは 2 つの Dojo Grid コンポーネントを使用しています。その 1 つは利用者のデータを保持するコンポーネント、そしてもう 1 つはインセンティブの結果を保持するコンポーネントです。Groovy 言語を使用すると、UI イベントに応答してサーバーまたはクライアントのいずれかでアプリケーションのイベントを作成することができます。Project Zero では、Ajax ライフサイクルを使ってアプリケーションのイベントを宣言するために以下の定義構文を使用します。
on("<UI_event>").fire("<application_event>").before
("<before_event>").after("<after_event>") |
| before_event | Ajax リクエストの前に起動するイベントを指定。ロード状況の表示、データ検証など。 |
|---|
| after_event | Ajax 要求の後に起動するイベントを指定。ロード状況の非表示、UI 更新など。 |
|---|
| application_event | 起動されるイベント。データの更新/フェッチ、UI の更新、あるいはカスタム biz/UI ロジックなど。 |
|---|
実行時には、イベントの処理は before_event --> application_event --> after_event の順に行われます。
Ajax ライフサイクルでは当然のことながら、application_event はサーバー側で処理されます。before_event と after_event を処理するのは通常、クライアント側です。詳細は、Writing Rich Web Applications を参照してください。
リスト 9
<html>
<head>
<title>Incentive Search</title>
<style type="text/css">
@import "incentiveSearch.css";
</style>
<script type="text/javascript" src="/dojo/dojo.js" djConfig="isDebug:
true, parseOnLoad: true"></script>
<script type="text/javascript">
dojo.require("dojox.grid.Grid");
dojo.require("dojox.grid._data.model");
dojo.require("dojo.data.ItemFileWriteStore");
dojo.require("dijit.form.ValidationTextBox");
dojo.require("dijit.form.DateTextBox");
dojo.require("dojox.grid._data.editors");
dojo.require("dojox.grid.editors");
dojo.require("dojox.grid._data.dijitEditors");
dojo.require("dojo.parser");
</script>
<%
on(".search:submit").fire("incentiveSearch").after("incentiveRender")
%>
</head>
<body class="tundra">
<h1> Consumer Incentive Search </h1>
<br>
<br>
<form class=.search>
<div id="consumerGrid" dojoType="dojox.Grid" structure="consumerLayout">
</div>
</form>
<br>
<br>
<hr>
<div id="incentiveGrid" dojoType="dojox.Grid" structure="incentiveLayout">
</div>
</body>
</html>
|
- public ディレクトリーに
incentiveSearch.groovy という別のファイルを作成します。このファイルに、incentiveSearch.groovy ファイルのクライアント・イベントに応答するイベント・ハンドラーを含めます。
図 98.
-
リスト 10 のコードを入力します (または、C:\ProjectZeroArticleSeries\Part2Artifacts\ConsumerAppArtifacts\conSnippet4.txt からこのコードを貼り付けます)。
onInitialize メソッドは、テンプレートがロードされると呼び出されます。利用者の情報を取得するためにリソース・モデルのローカル API が使われますが、その API はクライアント・ゾーンに書き込まれます。
リソース API についての詳細は、Programmatic Model API を参照してください。
それぞれのイベントごとに 1 つのイベント・ハンドラーがあります。イベント・ハンドラーはリクエスト・パラメーターの有無を調べてプロバイダー検索を行うか、ロケーション検索を行うかを決定します (接続についての詳細は、Using the Connection API を参照)。ここではペイロードをクライアント・ゾーンに変換するために、JSON API も使用しています。変換されたクライアント・ゾーンには、JavaScript クライアントとサーバー Groovy コードの両方がアクセスできます。
リスト 10
import zero.core.connection.*;
import zero.resource.*;
def onInitialize()
{
def collection = TypeCollection.retrieve('consumer');
def result = collection.list();
def items = [];
for(resultItem in result)
{
def item =
[name:resultItem.name ,id:resultItem.id ,state:resultItem.state,
provider:resultItem.provider ]
items.add(item);
}
client.data.consumer = items;
client.data.incentives = [];
}
def onIncentiveSearch()
{
if(event.input.location[])
{
Connection.Response response =
Connection.doGET("http://localhost:8080/resources/incentive?location=
${event.input.location[]}");
client.data.incentives =
zero.json.java.JSONArray.parse(response.getResponseBodyAsString());
}
else if(event.input.provider[])
{
Connection.Response response =
Connection.doGET("http://localhost:8080/resources/provider/
${event.input.provider[]}/incentive");
client.data.incentives =
zero.json.java.JSONArray.parse(response.getResponseBodyAsString());
}
else
{
client.data.incentives = [];
}
}
|
- public ディレクトリーに incentiveSearch.js という別のファイルを作成します。このファイルに、クライアント・イベントが含まれることになります。これらのコールバックを使用して、サーバー呼び出しの結果をレンダリングします。
図 99.
- リスト 11 のコードを入力します。このコードでは Dojo FileStore と Grid API の両方を使ってローカルでデータをロードします。Dojo Grid の使用方法についての詳細は、Grid の資料を参照してください。
リスト 11
formatDate = function(date) {
var d = new Date();
d.setTime(date);
return d;
}
formatLocationLink = function(location) {
var locationLink = '<input type="submit" name="location"
class="button linktd" value="';
locationLink += location;
locationLink += '"/>';
return locationLink;
}
formatProviderLink = function(provider) {
var providerLink = "<input type='submit' name='provider'
class='button linktd' value='";
providerLink += provider;
providerLink += "'/>";
return providerLink;
}
var consumerLayout = [{
cells: [[
{name: 'Id', field: "id", width:"10%"},
{name: 'Name', field: "name", width:"30%"},
{name: 'State', field: "state",
width:"35%",formatter:formatLocationLink},
{name: 'Provider', field: "provider",
width:"25%",formatter:formatProviderLink}
]
]
}];
var incentiveLayout = [{
cells: [[
{name: 'Provider', field: "providername",width:"20%"},
{name: 'Type', field: "incentive_type",width:"20%"},
{name: 'Provider Location', field: "location",width:"20%"},
{name: 'Valid From', field: "validfrom",width:"20%",formatter:formatDate},
{name: 'Valid To', field: "validto",width:"20%",formatter:formatDate}
]
]
}];
var consumerMeta =
{
id:'id',
items:[],
label:'consumer'
}
var incentiveMeta =
{
id:'incentiveid',
items:[],
label:'incentive'
}
function onLoad(){
zfire("renderConsumers");
}
function onRenderConsumers()
{
consumerMeta.items = dojo.clone(zget("/client/data/consumer"));
console.debug(consumerMeta.items);
var consumerStore = new dojo.data.ItemFileWriteStore({data: consumerMeta});
var consumerModel = new dojox.grid.data.DojoData();
consumerModel.store = consumerStore;
consumerModel.query = {id:'*'};
var consumerGrid = dijit.byId('consumerGrid');
consumerGrid.setModel(consumerModel);
}
function onIncentiveRender()
{
incentiveMeta.items = dojo.clone(zget("/client/data/incentives"));
console.debug(incentiveMeta.items);
var incentiveStore = new dojo.data.ItemFileWriteStore({data: incentiveMeta});
var incentiveModel = new dojox.grid.data.DojoData();
incentiveModel.store = incentiveStore;
incentiveModel.query = {incentiveid:'*'};
var incentiveGrid = dijit.byId('incentiveGrid');
incentiveGrid.setModel(incentiveModel);
}
|
- UI の拡張用に incentiveSearch.css ファイルが提供されています。C:\ProjectZeroArticleSeries\Part2Artifacts\ConsumerAppArtifacts\incentiveSearch.css を public ディレクトリーにインポートしてください。
図 100.
- UI を起動するには、ブラウザーを開いて http://localhost:8081/locationSearch.zhtml にアクセスします。すると、クライアントが以下の画面をレンダリングします。
図 101.
-
Texas リンクをクリックしてインセンティブ検索を開始します。
図 102.
- 同じように、それぞれのプロバイダーをクリックしてプロバイダー検索を開始してください。
図 103.
図 104.

 |
まとめ
今回の記事では、Zero の新しい概念をいくつか紹介するとともに、より複雑なデータを公開して URL ベースのリソースをセキュアにすることに重点を置いて、アプリケーション中心の設計を詳しく説明しました。
さらに、リソースをモデル化する新しい方法と Zero クライアント API についても理解してもらえたはずです。
次回はリソースのモデル化についてさらに詳しく説明し、Dojo 1.0 と Zero クライアント・プログラミング・モデルを使って、さらにソリューションを組み立てます。
ダウンロード | 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|
| Sample code for this article | Part2Artifacts.zip | 10KB | HTTP |
|---|
参考文献 学ぶために
製品や技術を入手するために
議論するために
|