Web サービス
Michael Wallent
Microsoft Corporation
March 6, 2000
日本語版最終更新日 2000 年 11 月 17 日
Web が引き起こした興味深い現象の 1 つに、ナンセンスとも思えるようなさまざまな言葉の造語能力の急激な発達があります。「Web サービス」というのも、そのような言葉の 1 つです (私のお気に入りは "SheCommerce" というやつですが)。「Web サービス」とはいったい何なんでしょうか? 明らかに、Amazon.com のようなサイトはサービスを提供しています。FedEx は荷物追跡サービスを提供しています。実際、Web 上のほぼすべてのサイトが、何らかのサービスを提供していると考えることができます。情報の提供は、物理的なもの (本や航空チケットなど) の提供がサービスであるのと同じように、サービスなのです。しかし、Web サービスを Web 上のあらゆるものとして定義してしまうと、先に進むことはできません。あらゆるものについて書くということは、Seinfeld の脚本を書くこととは正反対のことかもしれませんが、議論の取っ掛かりとしてはあまり有効ではないでしょう。
Web サービスは、複数のサービスを組み合わせて、何か新しいものを作り出すときに興味深いものとなります。たとえば、ショッピング サイトの比較サイトです。この種のサイトは、複数のソースからのデータを集めて、何らかの独自の価値を付加します。これは 1 つの例に過ぎません。その他にも、お気に入りのサイトから本をオンラインで購入し、注文状況と同じページで出荷状況を見るというような例が考えられます。いまや、Web サービスの新時代、データが王様となる時代なのです。
たとえば、この新しい Web サービスの世界では、これまで力が注がれてきたクールな HTML フロントエンドは、現在の在庫レベルに関するデータの収集を可能にする共通の XML スキーマと比較するとずっと価値が低いものになります。いまや、付加価値を提供するサイトは、最高のデータ収集サービスを提供するサイトなのです。最高の旅行サイトは、トレドへの往復チケットを一番安く提供できるだけでなく、ユーザーのカレンダーに情報を入力し、現地での最高のレストランに関する情報を収集してくれるサイトなのです。
ここにはいくつかの問題があります。サイトは、自らの重要なビジネス データをオープンに公開したいとは思わないかもしれません。価格と配達スケジュールだけで競争したいとは考えないかもしれません。依然として、カスタマの囲い込みという「古い」モデル (現在でも多くのケースでこれが「最新」の考え方だとされていることを知っている私としては、この表現を使うことにためらいを感じますが) に執着しているサイトもあるでしょう。この新しい試みは、自動車用アンテナのベンダが車のタイヤをパンクさせようとするようなものかもしれませんし、ベータと VHS のシナリオの再来なのかもしれません (Web サービスがベータ陣営に相当します)。いずれにせよ、カスタマにとっての潜在的な恩恵はきわめて大きく、上記のような障害があるにしても、Web サービスは検討に値する発想なのです。
重要な要因
効果的な Web サービスを実現するためには、まずデータを共通のメカニズムを使って公開する必要があります。ほとんどの場合は、XML と共通のスキーマの組み合わせが好まれます (BizTalk など)。データが得られたら、そのデータを要求に応じて作成できなくてはならず、更新が可能でなくてはなりません。データが生成されるときには、ビジュアルに提示できる必要があります。できるならば、複数のデータ セットを統合できると面白いでしょう。XSL とサーバー サイドのテクニックを使って視覚に訴える方法については、他の人が説明してくれるでしょう。この DHTML Dude では、これをクライアント上で行う方法を説明することにします。
11 月に、私は双方向の XML データ バインドの例を紹介しました。今回は、あのサンプルに少しばかりの変更を加えて、複数のデータ ソースを統合する方法を示し、「差分管理」モデルを具体化しました。
新しいサンプル
サンプルはここにあります。
持ち株のテーブルを取得し、これに株価を追加している方法に注目してください。ポートフォリオ情報と株価情報は、2 つの異なるソースから来ています。
データ バインドした テーブルでは、1 つのデータ ソース オブジェクトしかバインドすることができません。しかし、テーブルをネストさせれば、個々のテーブルが異なるデータ ソース参照を持つことができます。この例では、テーブルは次のようになります。
<table border=0 id=CustPortfolio datasrc=#portfolio
style="width: 95%; border: 1px solid black; ">
<col width=*>
<col width=100>
<col width=100>
<col width=100>
<thead>
<tr>
<td>Stock Name</td>
<td>Symbol</td>
<td>Held</td>
<td>Market Value</td>
</tr>
</thead>
<tbody>
<tr>
<td>
<div datafld=name></div>
</td>
<td>
<div id=ActiveSymbol datafld=symbol></div>
</td>
<td>
<div datafld=quantity></div>
</td>
<td>
<table id=OuterTable border=0
style="position: relative;">
<tr style="position: absolute; top: -11; left: 0;">
<td><span id=TheStock></span></td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
第 2 のテーブル (id=OuterTable のテーブル) に、第 2 のデータ ソース参照が挿入されます。個々の OuterTable は、対応する株式シンボル値を示す異なる dataFld プロパティを持つので、個々の行の dataFld プロパティは、行が追加された時点で設定されなくてはなりません。CustPortfolio テーブルの形状が変更されると、 init() メソッドが呼び出されます。 init() メソッドは OuterTable のすべてのインスタンスを繰り返し処理します (行が繰り返されると、OuterTable オブジェクトも繰り返されるので、インスタンスの数は多くなります)。OuterTable の個々のインスタンスについて、id=TheStock の <SPAN> が探され、その要素をパラメータとして setupStock() メソッドが呼び出されます。
function init() {
if (CustPortfolio.readyState == "complete") {
var t, o, i;
t = OuterTable;
for (i=0; i<OuterTable.length; i++) {
o = OuterTable[i];
setupStock(o.all.TheStock);
}
}
}
function setupStock(e) {
var y;
var stock;
try {
stock = e.parentElement.parentElement.parentElement.
parentElement.parentElement.parentElement.all.ActiveSymbol;
if (stock) {
if (e.set != "true") {
if (stock.innerText != "" ) {
e.dataFld = stock.innerText;
e.parentElement.parentElement.parentElement.
parentElement.dataSrc = "#stockvalues";
e.set = "true";
}
}
}
}
catch(x) {
}
}
setupStock() メソッドでは、現在の行のシンボルが決定されます ("stock" メンバが設定されているところにまで parentElement チェーンをたどっていきます)。行の株式シンボルが決定されたら、現在の行の OuterTable の dataSrc と dataFld が設定されます。これにより、株価のバインドが可能になります。
果たしてそれだけの価値があったのか
読者はこう思ったかもしれません。「なぜ、XML データ セットの中で値を調べて、手動で設定するという方法をとらないのか。 単にデータ バインドを設定するという目的には、あまりに面倒な処理なのではないか」。
たしかに、この処理は少し複雑です。しかし、そのおかげで素晴らしい利点が得られています。XML の中の値の変化をもとに、自動的にポートフォリオを更新することができるのです。株価が変わると、新しい値が表示されます。新しいポートフォリオ情報が入力されると、やはり自動的に更新が行われます。
ページの下部に、[Stocks Value Change] と [Portfolio Change] の 2 つのボタンがあることに気づいたことでしょう。[Portfolio Change] ボタンをクリックしてみてください。2 つの行が挿入され、"Aardvark Industries" の "Held" 列が変化します。
では、これがどのようにして起こったのかを見てみましょう。
function updatePortfolio() {
portfoliodelta.ondatasetcomplete = doPortfolioUpdate;
portfoliodelta.load("portfolio4d.xml");
}
function doPortfolioUpdate() {
var xmld, xmld2;
xmld = portfoliodelta.XMLDocument.documentElement;
xmld2 = portfolio.XMLDocument.documentElement;
doUpdate(xmld, xmld2);
}
function doUpdate(xmld, xmld2) {
var fc, nfc, i, j;
for (i=0; i<xmld.childNodes.length; i++) {
fc = xmld.childNodes[i];
updateNode = findMatchingNode(xmld2, fc);
if (!updateNode) {
nfc = fc.cloneNode(true);
xmld2.insertBefore(nfc, null);
} else {
for (j=0; j<fc.attributes.length; j++) {
updateNode.attributes[j].nodeValue =
fc.attributes[j].nodeValue;
}
}
}
}
function findMatchingNode(xmld2, node) {
var i, fc;
for (i=0; i<xmld2.childNodes.length; i++) {
fc = xmld2.childNodes[i];
if ((fc.attributes[0].nodeName == node.attributes[0].nodeName) &&
(fc.attributes[0].nodeValue == node.attributes[0].nodeValue))
return fc;
}
return null;
}
文書の先頭には、4 つのデータ アイランドがあります。2 つは、ポートフォリオと株式データの初期データ セットを含んでおり、2 つの「差分」のデータ アイランドです。差分 データ アイランドは、差分 データ セットをロードするために使用されます。これらのデータ セットは、テーブルがバインドされているプライマリ データ セットと比較され、統合されます。ノード値が変化したり、ノードの追加または削除が行われると、バインドされたビューは自動的に更新されます。この自動更新こそが、株価をバインドするために面倒な作業を行った結果として得られる利点なのです。
[Update Portfolio] ボタンがクリックされると、差分 XML ファイルがロードされます。データがロードされると、doUpdate () メソッドがツリーを渡り歩き、変更点を探して、プライマリ データ ツリーに適用します。
次に、[Stocks Value Change] ボタンの動作を見てみましょう。差分 XML ファイルを変更することで、さまざまな変更の実験を行うことができます。差分 ファイルは、データ セット全体またはページ全体を再ロードするのと比べれば、きわめて小さいことに注意してください。これはリッチ クライアントの便利な点の 1 つです。
結論
Web サービスのゲームは、どちらの側にも参加することができます。自分の持っているデータを XML 共通スキーマとして公開することもできますし、データを統合してユーザーにとって興味深いシナリオを作成するクールなやり方を考えることもできます。データを一種の商品のように捉えることには、いくぶん怖い面もありますが、カスタマにとっては明らかに大きな利点となります。そして結局のところ、われわれの仕事はカスタマにサービスを提供することなのです。
|