平成 14 年度 卒業論文 「Web サービスによるナレッジマネジメント」 〜研究室における知識資源のナレッジベース化〜 学籍番号:199801097 所属:筑波大学第三学群社会工学類 主専攻:経営工学 氏名:前田 杏奈 指導教官:佐藤 亮 1. 目的 本研究の目的は Web サービスという新しい Web 技術への理解を深めること、そして Web サービスを利用したナレッジベースのシステムモデルを構築することである。 2. 特色 ナレッジベースのシステムモデルを構築するにあたって、Web サービスという新技術を 導入した。Web サービスとはインターネットプロトコルによってソフトウェアシステムを 連携する技術のことである。 システムを設計するにあたって、UML というオブジェクト指向方法論を取り入れ分析を 行った。Web サービスを UML でどのように扱ったらよいか新たな提案も行っている。そ して Microsoft 社の Web サービス開発環境 .NET Framework のもとでシステムの実装を 行った。 3. 結論 Web サービスへの理解を深め、Web サービスを利用した研究室における知識資源を有効 活用するナレッジベースを構築した。ナレッジベースにおけるデータの取得・更新、認証 などの機能を Web サービス化し、これらの機能を利用するクライアントアプリケーション を構築した。Web サービスを導入することによって、サーバ、クライアント間の連携を自 動化することができた。 平成 14 年度 卒業論文 Web サービスによるナレッジマネジメント 研究室における知識資源のナレッジベース化 筑波大学第三学群社会工学類経営工学専攻 4 年 学籍番号 199801097 前田 担当指導教官 杏奈 佐藤 2003 年 1 月 29 日 亮 序論 この研究は雑然とした研究室の中で、あるパラドックスから生まれた。研究室に所属す るようになって半年強、毎週行われるゼミ。ここが唯一の知識交換の場である。「○○の CD-ROM 誰が持ってる?」「○○のパスワード何だっけ?」数日間の研究でそれぞれに浮 上した疑問がここで飛び交う。「どこかに書いてるよ。」「誰かが知ってるよ。」これらを一 声とする会話が毎週繰り返される。確かに研究室の部屋の中を見渡すとどこかにある、あ るいは誰かが知っている。たとえばそれはホワイトボードの隅に書いてあったり、棚の上 のダンボールに入っていたりする。しかしどこにあるのか、あるいは誰が知っているのか は毎週のゼミ、ミーティングの場でしか明らかにされない。 ここは経営システム工学研究室である。所属する人たちの研究テーマはまさにそのとお り、 ○○による生産改善シミュレーション 、 基幹業務パッケージにおける○○ などビ ジネスプロセス工学に関わる研究が多い。果たして研究室の中はどうだろう。情報資源、 知識資源などはほとんど管理されていないのが現状である。 このパラドックスを解くかもしれない鍵もまた乱雑な研究室の中にあった。ゼミの後机 にうず高く積まれている各種雑誌・学会誌などをパラパラと流し読みしていたところ、よ く見かけるキーワードがあった。「Web サービス」である。 Web サービスがビジネスモデ ルを変える 、 Web サービスがもたらすビジネス革新 、 Web サービスを支えるテクノロ ジー 、さらに Web サービスはこんなに簡単! などの記事のタイトルが私の興味をそそ った。何かができそうだと感じた。今非常に関心の高い「Web サービス」は毎月のように 雑誌に取り上げられている。しかしまだまだ事例が少なく、技術に関する記事や情報ばか りが目に付く。そのような現状にチャレンジしてみようと考えた。 まず「Web サービス」を理解し何ができるかを探ること、そして「Web サービス」を利 用して研究室内の知識資源を有効活用するシステムモデルを構築することを目標に研究に 取り組む。 1 目次 序論 ................................................................1 目次 ........................................................... .....2 第1章 Web サービスとは ............................................3 1.1.Web サービスの標準化 ... .. ... ... ... .. ... ... ... .. ... ... ... .. ... .. .3 1.2.Web サービスの概要 .. ... .. ... ... ... .. ... ... ... .. ... ... ... .. ... .. .4 第2章 研究室における知識資源 .. ................................ .....8 2.1.現状 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .8 2.2.提案 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .9 2.3.企画 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .10 第3章 Lab ナレッジベースの分析と設計 .......................... ....12 3.1.Lab ナレッジベースの構想 . ... ... ... .. ... ... ... .. ... ... ... .. ... ..12 3.2.要素分析 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .13 3.3.仕様分析 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .20 第4章 Lab ナレッジベースの実装 ....................................27 4.1.実装環境と製品概要 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .27 4.2.Web サービスの実装 .. ... .. ... ... ... .. ... ... ... .. ... ... ... .. ... ..28 4.3.WebLabDB Web サービスの実装 ... .. ... ... ... .. ... ... ... .. ... ... .31 4.4.WinLabDB Windows クライアントの実装 . ... ... .. ... ... ... .. ... .. .39 4.5.Lab ナレッジベースの稼動 . ... ... ... .. ... ... ... .. ... ... ... .. ... ..42 第5章 結論 ........................................................47 謝辞 ........................................................... ....49 参考文献 ...........................................................50 2 第1章 Web サービスとは 「Web サービス」という言葉が IT 業界で注目を浴びている。そのうたい文句は「インタ ーネット上の様々な Web サイトを ソフトウェア部品 とみなし、自由自在に組み合わせ て一つのシステムを構築できる」、 「企業は Web サービスによって情報システムを実現する ようになる。従来のように自社でシステムを構築・運用する必要がなくなる」、「相当額の 開発費が必要な現状に比べ、システム関連コストは大幅に安くなる」と華やかである。そ れでは Web サービスとは一体どのようなものなのであろう。 1.1.Web サービスの標準化 大手ベンダーの何社かは、Web サービスを「ある Web アプリケーションから呼び出して 利用できる別の Web アプリケーション、あるいは巨大なコンポーネント」と説明する。そ の一方で、Web サービスを「次世代の情報システム・アーキテクチャ、あるいは概念」と いう人もいる。このように Web サービスに対する解釈は様々でイメージがつかみにくい。 そもそも「サービス」という言葉自体が曖昧で、いろいろな捉え方ができることも Web サ ービスの解釈を混乱させている。言えることは、 Web サービスは Web 技術の発展形であり、 今まさに進化し続けているということである。(参考:[8]) Web 技術標準化団体 W3C(World Wide Web Consortium)では、様々な Web 技術に対し てそれぞれワーキンググループを形成し、技術の標準化に努めている。W3C は Web サービ スに対して4つのワーキンググループを形成しており、その中の1つ Web Services Architecture Working Group は最新(2003 年 1 月 16 日現在)のワーキングドラフトで Web サービスを以下のように定義している。 [Definition] A Web service is a software system identified by a URI, whose public interfaces and bindings are defined and described using XML. Its definition can be discovered by other software systems. These systems may then interact with the Web service in a manner prescribed by its definition, using XML based messages conveyed by Internet protocols [22]. Web サービスは URI(Uniform Resource Identifier)によって識別されるソフトウェアシ 3 ステムである。そしてそのインタフェース(ソフトウェア間で相手のプログラムコードを 呼び出すための規約)やバインディング(プログラム同士の結合)情報は XML(eXtensible Markup Language)によって定義・登録され公開されるので、他のソフトウェアシステムか ら検索することができる。これらのソフトウェアシステムは規定された規則にしたがって、 インターネットプロトコルを介して Web サービスと XML 書式のメッセージをやり取りす る。 Web サービスの標準化は今まさに進んでいるところである。本論文では、この W3C によ る 2002 年 11 月の定義を採用して話を進める。 このように Web サービスはインターネットプロトコルによってソフトウェアシステムを 連携する Web 技術であり、またそのソフトウェアシステムでもある。つまり Web サービス はインターネット上に分散するアプリケーションの提供する機能を、クライアントのアプ リケーションにプログラミング可能なインタフェースとして提供することができる。シス テム間連携の自動化こそが Web サービスの生まれた所以であり、Web サービスが注目され る理由でもある。 1.2.Web サービスの概要 本節では Web サービスを構成する要素技術と Web サービスの概要を説明する。なおこの 節は玉置[8]による 「Web サービス」を基礎から理解する を参考にしている。Web サー ビスの主な要素技術は SOAP、WSDL、UDDI の 3 つである。まずソフトウェアシステム 同士でデータをやりとりしたり、特定の処理プログラムを呼び出したりする中核の技術が SOAP である。 1.2.1.SOAP SOAP (Simple Object Access Protocol)はソフトウェアシステム同士のメッセージ交換用 のプロトコル(通信規約)である。メッセージの配送手順や受信メッセージの処理手順な どを定めている。 特徴 (1) メッセージを伝送する通信プロトコルとして HTTP を利用できること。 コンピュータネットワークは通常、インターネットなど外部からの不正侵入を遮断する ためにファイアウォールを使って、HTTP など特定の通信プロトコルだけを通すようにし ている。SOAP メッセージは HTTP を使って伝送するので、既存のファイアウォール設定 4 を変更しないで利用することができる。 (2) やり取りするデータの表現方法として XML を採用したこと。 XML(eXtensible Markup Language):自由に定義可能な構成要素の組み合わせで文書や データを記述するための仕様。ユーザが独自のタグを指定できるメタ言語の一種で、デー タをネットワーク経由で送受信するための言語として利用されている。 XML は個々のデータの意味を記述できるので、データを受け取ったソフトウェアシステ ムは意味を取り違えることなく処理することができる。また XML はインターネット上のデ ータ表現手段として普及しているところなので、既存の Web サイトとの親和性が高い。 (3) 仕組みがシンプル SOAP が定めているのは、Web サービス間で受け渡すメッセージ(SOAP メッセージ) のフォーマットとその処理手順だけである。処理手順とは、SOAP メッセージから XML 形 式のデータを抽出する手順、処理結果を送信元に返すかどうかなどといった規定のことで ある。 1.2.2.WSDL SOAP メッセージを完成するには、相手先 Web サイトの URL、提供するサービス(処理) の名称、各処理に受け渡すデータの型、利用する通信プロトコルなどの情報が必要である。 これらの情報のフォーマットを定めたのが、2 つ目の要素技術 WSDL である。 WSDL (Web Services Description Language)はインタフェースとバインディング情報の 記述方法を定めた約束事(フォーマット)である。このフォーマットにしたがって Web サ ービスの情報を記述した XML ファイルを作り、SOAP メッセージを生成する。 1.2.3.Web サービスの流れ それでは SOAP や WSDL を使ってどのようにソフトウェアシステム間の連携が行われて いるのか、Web サービスの処理の流れを示す(図 1.1)。例として英語を他の言語に変換す る Web サービスを想定する。特に XX Web サービスは英語を日本語に変換する Web サー ビスであるとする。 5 Webサービスの処理の流れ AA Webサービス SOAPメッセージ 生成モジュール SOAPメッセージ を生成 アプリケーション 受信したSOAPメッセージ からXMLデータを抽出 SOAPメッセージ (処理結果) XML データを SOAPメッセージ 抽出 解析モジュール XMLパーサ SOAPメッセージ 解析モジュール HTTP を使って 伝送 Webサーバ XML データ SOAPメッセージ 生成モジュール XX Webサービス WSDLファイル Webサービスの内容 SOAPメッセージ を記述したファイル (処理要求) Webサーバ 結果 を表示 XMLパーサ 一般利用者 あらかじめ 取得 参照 アプリケーション データ を入力 WSDLファイル Webサービスの内容 を記述したファイル 処理結果 をXML データに変換 XMLパーサ:XML文書をアプリケーションソフトが利用しやすい形に変換するソフトウェア 図 1.1 Web サービスの処理の流れ([8]の図 4 を元に作成) 一般利用者が言語変換アプリケーションを利用してデータ「Hello」を入力すると AA Web サービスはそのデータを XX Web サービスへ送信する。XX Web サービスはデータを受け 取り、英語日本語変換アプリケーションを利用してデータを変換し、その結果「こんにち は」を AA Web サービスへ返す。AA Web サービスはその結果を言語変換アプリケーショ ンに表示する。AA Web サービスは同様に YY Web サービス、ZZ Web サービスなどを利用 して英語をフランス語、ドイツ語などに変換するサービスを提供することができる。 この処理を Web サービスの動作から見ると次のようになる。利用者がアプリケーション から入力したデータを XML パーサは XML 形式に変換し、SOAP メッセージ生成モジュー ルへこの XML データを引き渡す。SOAP メッセージ生成モジュールは相手先の Web サー ビスの情報を記した WSDL を参照し、XML データから SOAP メッセージを生成する。次 に SOAP メッセージ生成モジュールは WSDL に記述してある通信プロトコルを使って、相 手先の Web サービスへ SOAP メッセージを送信する。SOAP メッセージを受け取った Web サービスは SOAP メッセージ解析モジュールを起動し、SOAP メッセージの内容から XML データを解析する。その後、適切な処理を実行できるアプリケーションへ、解析結果であ る処理要求とデータを引き渡す。処理結果は、これまでと逆の順序をたどって呼び出しも との Web サービスへと戻り、最終的に利用者のアプリケーションの表示に反映される。 このように SOAP と WSDL を使ってソフトウェア間の連携が行われるのである。それで はそもそも相手先の Web サービス情報が記された WSDL ファイルはどのように取得する のだろうか。サービスが既知の場合はこれを事前に入手しておけるが、未知の場合は入手 6 しなければない。そこで WSDL ファイルを入手可能にするのが 3 つ目の要素技術 UDDI である。 1.2.4.UDDI UDDI (Universal Description, Discovery, and Integration)は Web サービスの情報を登 録したり、検索したりするためのデータベース仕様(レジストリ)のことである。実際に はレジストリサービス提供者が UDDI 仕様に基づくデータベースを構築し、Web サービス 提供者がそこに情報を登録する。Web サービスの利用者はこのデータベースを使って Web サービスの所在や内容を検索する。こうしてインターネット上に分散した Web サービスの 中から、必要な Web サービスを見つけ出すことができるのだ。このように様々な要素技術 を使って Web サービスが実現される。 7 第2章 研究室における知識資源 ここでは一度 Web サービスとは離れて、研究室における知識資源について考察する。 2.1.現状 2.1.1.プリンタ設定時のエピソード 研究室では一つのプリンタをいくつかのコンピュータが共有して使っている。新しく買 ったコンピュータを同じようにプリンタに接続しようと、4 年生の二人は作業していた。し かしプリンタと複数のコンピュータがどのように接続されているのか、二人はまったく知 らなかった。二人はまずコンピュータに新しくプリンタを追加する設定を試したが、最後 まで設定することはできなかった。追加するプリンタがローカルプリンタなのかネットワ ークプリンタなのかわからなかったので、細かな条件をすべて設定することができなかっ たからだ。その後二人はプリンタの説明書、コンピュータのヘルプメニューやインターネ ットを利用して、プリンタの接続方法について調べた。そしてプリンタドライバのインス トールが必要なことやネットワークパスの設定方法などもわかった。しかしそれでも自力 でプリンタを設定することはできなかった。 そして翌日、4年生の二人はゼミで研究室にやってきた A 先輩にプリンタの設定につい て尋ねた。A 先輩はあっさりと答えてくれた。 「あぁ、プリンタね。プリンタの下にダンボ ールあるでしょ。その中に CD-ROM 入っているよ。あれ使って。」そこに、遅れてやって きた先生が付け加える。 「設定用の CD-ROM を2枚使うのです。一つでプリンタドライブ をインストールして、もう一つでポートを設定するのです。そして最後に設定したポート にプリンタを接続するのです。」4年生の二人はポートを設定することや CD-ROM が必要 なことを(もちろん CD-ROM が置いてある場所も)知らなかったのだ。ゼミが終わった後、 二人は早速 CD-ROM を使ってプリンタを設定した。作業はスムーズに行なわれ、5分ほど で無事設定を終えた。 2.1.2.考察 このようにプリンタの設定には①2枚の CD-ROM を使用すること、②プリンタドライバ の設定とポートの設定が必要なこと、この2つの知識が必要であった。そしてこれらの知 8 識を持っていたのは研究室の A 先輩と先生である。加えて言えば、プリンタを設定したい 4年生二人は、プリンタの設定に関する知識を A 先輩と先生が持っているということを知 る必要があったのだ。 わからないことを解決しようとするとき、私たちは本や資料、インターネットを調べる ことが多い。そこで得られる情報は確かに役立つ。しかしそこでの情報は広く世間に公開 されているものなので、限定された場所でどのように応用するのかわからないことも多い。 情報というのは、その情報が生まれた、または利用される場において、本当に役に立つの ではないだろうか。 「知識」とは人が何かの経験を通じて正当化した信念である。「情報」とはデータから構 成された意味や意義であるのに対して、「知識」とは情報を認識し、行動に至らしめる秩序 なのである[3]。前の話で 4 年生がインターネットなどで調べてわかったことは「情報」で あり、先輩や先生から教わってわかったことは「知識」だと言えるのではないだろうか。 「知 識」は人や人の経験に依存するので、どこでどのような状況で言えるのかも大事である。 よって、自分たちのいる研究室という状況(「場」)の中で共有される「知識」は非常に有 用である。 このような「知識」を経営の中で活かしていくアイディアとしてナレッジマネジメント がある。ナレッジマネジメントは、「知識管理」とも「知識経営」とも訳すことができる。 紺野[3]によると前者の「知識管理」は狭義の意味であり、ナレッジマネジメントを企業の 知識資産を有効活用するための手法と捉えている。これに対して後者の「知識経営」は、 経営の中で「知識」を活かすという意味であり、テクノロジーの変化、スピード経営など あらゆる変化の中で「知識」の本質を捉え、新たな仕組みを作っていこうとするものであ る。 2.2.提案 企業経営におけるナレッジマネジメントのアイディアは、研究室の現状にも応用できる のではないだろうか。特に「知識管理」のアイディアはそのまま生かすことができる。研 究室に所属する人たちがそれぞれ経験した知識を管理すれば、他の人たちもその知識を利 用することができるだろう。そしてもちろん「知識経営」という視点からも、研究室に蓄 積された知識資源から新たな研究を創造し、さらにはクリエイティブな研究者を輩出する という意味で有用であると考えられる。 今私たちの研究室に蓄積されている知識資源はほぼないに等しい。ほとんどの知識資源 9 は研究室に所属する人それぞれに記憶されている。研究室内のわずかな知識資源の蓄積と して上げられるのは、そこここに貼られている付箋だけである。知りたいことがあるとき は、知っている人を探さなければならないし、付箋は時間が経つと剥がれ落ち、ごみとな るばかりである。なんとかして研究室にある知識資源を形のあるもとして蓄積し、管理す るシステムを作りたいと思った。 そこで思い立ったのが Web サービスによるナレッジマネジメントである。今回の研究で は特に「知識管理」という意味に重きをおいて、研究室における知識資源を管理し、有効 活用することを目指すナレッジベースを開発することにする。 2.3.企画 今までのデータベースシステムとは違って、(1)「場」に応じた情報を管理する、 (2) (人格のある)人の知識を再現する、(3)人が使いやすいナレッジベースを作りたいと思 う。 (1) 「場」に応じた情報を管理する 場に応じた情報は、細かく詳しくなる。例えば OS によって方法が異なるソフトウェアの インストール方法や研究室に十数台あるパソコンごとに設定されたユーザ情報など、その 形式は定義しづらい。よってデータは決まった型にはめ込むよりは、柔軟に対応できるよ うにしたい。 (2) 人の知識を再現する 研究室に所属する人が自身の経験によって得る知識は、まさにその人に依存する。例え ば自宅でノートパソコンを使いホームページを作成している B 君にとって USB の接続方法 は常識であるかもしれない。しかし普段もっぱらデスクトップパソコンでネットサーフィ ンをしている C 君にとっては、USB 接続による MO の使い方は未知の情報で有益であるだ ろう。このようにどのような情報が有益であるかは人それぞれである。ナレッジベースで はあくまで人が主観的に有用だと思うことをデータとしたい。 また研究室では所属する人たちは顔見知りであり、さらに研究内容やゼミでの取り組み などを通して個性的な人格のある人になる。G さんはシステム開発の経験があるとか、K さんはビジネスモデルについて詳しい、などといったそれぞれの人のバックグランドがわ かるかもしれない。また E さんは物事をイメージで捉えることが多いとか、N さんはいつ も物事を順序立てて考える、などといった思考方法までもわかるようになるかもしれない。 ナレッジベースではこのような、「あの人ならわかるかもしれない」であるとか、「あの人 10 ならこの部分を納得させてくれるかもしれない」などといった人格を意識した探索をさせ たい。 (3) 人が使いやすい 人が使いやすいユーザインタフェースの開発は、今まで常に考えられてきたことであり 発展を遂げている。しかし究極に使いやすいのは、まさに一人一人個別に開発されたユー ザインタフェースである。基本的な機能をある程度規定したら、ユーザがそれらの機能を 自分がもっとも使いやすいスタイルにして利用したい。 以上の 3 点を達成するシステムをつくることを目標とする。これらを実現するためには どのようにしたらよいのか、思いつくアイディアをあげておく。 (1)を達成するためには、データモデルは細かくデータ定義をしないようにする。デ ータの内容はテキストで入力するようにして、さらにノートのように自由に書き込めるよ うにする。その代わり、データ内容の概要がわかるような項目を設け、必要であればその データの内容を閲覧するという形式にする。 (2)は非常に概念的である。まずは、データの登録・編集は知識を持つ人本人が行う こととする。そして知識を人の属するものと捉え、誰の持つ知識なのかを明確にする。 (3)を実現するのは Web サービスの特徴の一つでもある。Web サービスは機能をユー ザがプログラミング可能なインタフェースとして提供するのであるから、ユーザは独自に ユーザインタフェースを開発することができる。 それではこれらのアイディアをもとに、次章から Lab ナレッジベースの分析と設計を行 う。 11 第3章 Lab ナレッジベースの分析と設計 3.1.Lab ナレッジベースの構想 研究室における知識資源を管理するナレッジベースを設計する。このとき特に(1)場 に応じた情報を管理する、(2)人の知識を再現する、(3)人が使いやすいシステムにな るように考える。構想アイディアは以下の図 3.1 のようになる。 Labナレッジベース Webサービス データベース Windowsアプリケーション Labナレッジベース データ項目を選択すると、 その詳細が見られる。 詳細表示画面 前田 ソフトウェア VS.NET 2002/12/20 井田 論文 Webサービス 2002/12/15 前田 プリンタ 設定方法 2002/12/7 VS.NET VS.NETのインストールは、付属の CD-ROMを使って行う。 使用環境 Windows 2000 Windows XP ナレッジデータの概要を一覧表示する 図 3.1 Lab ナレッジベースの構想 ナレッジベースの主な機能は、知識データの登録・編集・閲覧とする。まずユーザは自 分の知識データをデータベースに登録する。ユーザは以前入力したデータの内容を編集す ることもできる。そして他のユーザはそれらのデータを閲覧することができる。クライア ントアプリケーションでは、メインの画面にデータの概要が一覧表示され、その中のデー タ項目を選択するとそのデータの詳しい内容が表示され、そこでデータの登録や編集がで きるようにする。 ナレッジベースでは基本となる機能の一部を Web サービス化し、使いやすいクライアン トアプリケーションを作る。さまざまなクライアントアプリケーションを作ることが可能 だ。また他の Web サービスを利用したり、他のシステムと連携したりすることも可能だ(図 3.2)。よって、ユーザは自分専用のクライアントアプリケーションを作りかえることもでき 12 るし、他の Web サービス機能を追加したりすることもできる。さらには他の研究室がこれ ら Web サービスを利用し、大学を超えた研究室間で連携する研究機関ナレッジベースシス テムを作ることも可能になるだろう。 データベース Webサービス サーバ Webアプリケーション 研究室PC1 研究室PC2 研究室PC3 自宅ノートPC Winアプ Winアプリケーション ver.1 図 3.2 Winアプリケーション ver.1 Winアプリケーション ver.2 Lab ナレッジベース Web サービスの構造 ここからは分析作業について述べていく。なお分析手法は吉田他[5]による「UML による オブジェクト指向開発実践ガイド」を参考にしており、各分析における目的の記述はこれ を引用している。また分析で使われる UML は Joseph[2]による「独習 UML 改訂版」など を参考にした。 UML(Unified Modeling Language):OMG(Object Management Group)が標準化してい るオブジェクト指向方法論。UML は扱おうとしている問題を明らかにし、その解決法を組 み立てる際に、問題を図や文章でモデリングするのに役立つ。誰が読んでも理解できるよ うに表現するので、システム開発時に生じる開発者とユーザとの間のコミュニケーション の隔たりを解消することができる。 3.2.要求分析 要求分析の目的は、システム要求を具体化し、開発するシステムが置かれる外部環境と その中での使われ方を明らかにし、システムの境界を確定することである。 3.2.1.ユースケース分析 13 システム化の範囲を決定し、システムが持つべき機能要件を明らかにする。つまり利用 者からみたシステムの機能(ユースケース)を列挙し、利用者または外部システムとの間 の具体的なインタラクション(シナリオ)を規定する。 (1) ユースケース図 ユースケース図はシステムの仕様機能(ユースケース)と外部環境(アクター)の関連 を表すものである。人型のアクターと楕円形で囲まれたユースケースをシステムの外部と 内部に配置することにより、一目でシステムの機能やシステムの外部と内部の境界を理解 することができる。ユースケースとは、システムがアクターに提供する機能であり、アク ターはユースケースを起動する人やものである。 Labナレッジベース データ登録 データ編集 ユーザ 図 3.3 データ閲覧 分析初期のユースケース図 ナレッジベースの基本機能はデータの登録・編集・閲覧である。最初の段階でのユースケ ース図は図 3.3 のようになる。しかしこれだけでは不十分であるので、補助機能を洗い出す 必要がある。まずデータの登録・編集はそのデータを所持するものしかできないようにす る必要があるので、ユーザを認識するために、ユーザ登録をするようにする。よってユー ザ登録機能、ログイン処理(ユーザ認証)機能を新たに加える。さらに機能どうしのつな がりをわかりやすくするために<<ステレオタイプ>>を記述する。<<extends>>は拡張関係、 <<uses>>は使用関係、<<include>>は包含関係を表す。このようにして作成したユースケ ース図が図 3.4 である。 ゲストユーザはまずユーザ登録をするかユーザ一覧を表示する。そしてユーザ一覧を利 用してつぎに Lab データ一覧を表示する。もし詳細データを見たければ、この後 Lab デー タ一覧から詳細 Lab データを閲覧する。既定ユーザはまずログインする。ログインはユー ザ認証を含んでおり、ログインすると必然的にそのユーザの Lab データ一覧を表示する。 そしてそのデータ一覧から該当項目を選び Lab データの登録や編集を行う。これらが Lab ナレッジベースの機能用件となる。 14 Labナレッジベース ユーザ登録 ユーザ一覧表示 <<uses>> ゲストユーザ Labデータ一覧表示 <<extends>> 詳細Labデータ閲覧 既定ユーザ ユーザ認証 Labデータ登録 <<include>> <<uses>> ログイン <<include>> Labデータ一覧表示 <<uses>> Labデータ編集 図 3.4 Lab ナレッジベースのユースケース図 それでは次にこれらユースケースの処理内容を考え、クライアントアプリケーションが すべきことと Web サービスがしたほうがよいことと機能を分離していく。例えば一覧を表 示するとき、アプリケーション画面にデータを表示するのはシステムが外部に行うことな のでクライアントの機能である。またこのとき、表示するデータを取得するのはシステム 内部ですることなので Web サービスの機能とすることができる。このように機能を分離し て得られたユースケース図は図 3.5 のようになる。 Lab ナレッジベースをクライアントアプリケーションと Web サービスとにシステム分解 した。名前はそれぞれ WinLabDB、WebLabDB としてある。Lab ナレッジベースでは主に WinLabDB はユーザ側とのシステム境界を受け持ち、WebLabDB はシステムの内部機能を 担当することになる。 15 Labナレッジベース クライアントアプリケーション (WinLabDB) Webサービス (WebLabDB) ユーザ登録 <<include>> ユーザ情報取得 <<uses>> ユーザ一覧表示 Labデータ一覧表示 <<include>> ゲストユーザ <<extends>> <<include>> Labデータ取得 詳細Labデータ閲覧 Labデータ登録 <<uses>> 既定ユーザ <<uses>> <<include>> <<include>> ログイン Labデータ更新 Labデータ一覧表示 <<uses>> <<uses>> Labデータ編集 <<include>> ユーザ認証 図 3.5 Web サービスを考慮した Lab ナレッジベースのユースケース図 (2) ユースケース記述 ユースケース図の中のユースケースごとにアクターとシステムの間のインタラクション を記述する。なお扱う情報を大まかに把握するために、途中簡単なクラス図を用いている。 クラス図の詳細については後述するので、ここでは触れないことにする。 ユースケース記述項目の説明 Use Case: 簡単な名前を付ける。管理 ID を明記する。 Summary: 機能の概要を簡単な文章で記述する。 Actors: このユースケースを利用するアクターを列挙する。 Preconditions: このユースケースを起動するときに成立している前提条件を記述する。 Descriptions: アクターとシステムの間のインタラクションを記述する。 @@で括った単語はアクターが実行するコマンドである。 Exceptions: どんな例外が発生するのか、またそのときにどう処理するのかを記述する。 16 Postconditions: このユースケースが終了した後に成立している条件を記述する。 ユースケース:ユーザ登録 Use Case: Lab ナレッジベース::WinLabDB::ユーザ登録(UA-UC01) Summary: ユーザ登録をする。 Actors: ゲストユーザ Preconditions: なし Descriptions: 1.ユーザはユーザ情報(「ユーザ ID」、 「パスワード」、「学籍番号」、「E-Mail アドレス」) を入力する。 2.@送信@ボタンを押す。 3.システムはユーザ情報をユーザ情報データベースへ格納する。 Exceptions: ユーザ ID が登録済み パスワードが無効 Postconditions: ユーザ情報データベースにユーザが登録される。 ユースケース:ユーザ一覧表示 Use Case: Lab ナレッジベース::WinLabDB::ユーザ一覧表示(GU-UC01) Summary: 登録ユーザの一覧を表示する。 Actors: ゲストユーザ、既定ユーザ Preconditions: なし Descriptions: 1.ユーザは@ユーザ一覧@ボタンを押す。 2.WinLabDB は WebLabDB のユーザ情報取得機能を呼び出す。 3.WebLabDB はユーザ情報データベースからユーザ ID を取り出し、WinLabDB に返す。 4.WinLabDB はユーザ ID をリスト表示する。 Exceptions: ユーザが登録されていない。 Postconditions: ユーザ一覧が表示される。 ユースケース:ログイン Use Case: Lab ナレッジベース::WinLabDB::ログイン(LG-UC01) Summary: 既定ユーザがクライアントアプリケーションにログインする。 Actors: 既定ユーザ Precondition: 既定ユーザはユーザ登録が済んでいる。 Descriptions: 17 1.ユーザは「ユーザ ID」と「パスワード」を入力し、@OK@ボタンを押す。 2.WinLabDB は WebLabDB のユーザ認証機能を呼び出す。 3.WebLabDB はユーザ情報データベースの中のユーザ ID とパスワードを確認し、 WinLabDB に承認・非承認を返す。 4.承認であれば、WinLabDB はユーザをログインさせる。非承認であれば、WinLabDB はログインエラーを表示する。 Exceptions: なし Postconditions: 既定ユーザをログインさせる。またはログインエラーを表示する。 ユーザ登録 -ユーザID -パスワード -学籍番号 -E-Mailアドレス +登録する() ユーザ情報データベース -ユーザID -パスワード -学籍番号 -E-Mailアドレス +格納する() +取り出す() ユーザ一覧表示 -ユーザID +リスト表示する() ログイン -ユーザID -パスワード +ユーザを認証する() 図 3.6 ユーザ認証に関わるクラス図 ユースケース:Lab データ一覧表示 Use Case: Lab ナレッジベース::WinLabDB::Lab データ一覧表示(GD-UC01) Summary: 指定ユーザの Lab データの概要を一覧表示する。 Actors: ゲストユーザ、既定ユーザ Preconditions: なし Descriptions: 1.ゲストユーザはユーザ一覧の中から@ユーザを指定してクリック@する。既定ユーザ はログインする。 2.WinLabDB は WebLabDB の Lab データ取得機能を呼び出す。 3.WebLabDB はユーザ情報データベースから指定ユーザのデータファイル名を取り出 す。 4.WebLabDB は指定ユーザの Lab データベースから、Lab データの概要(「分類」 、 「タ イトル」、「更新日」)を取り出し、WinLabDB に返す。 5.WinLabDB は Lab データの概要をデータグリッドに一覧表示する。 Exceptions: データが登録されていない。 18 Postconditions: 指定ユーザの Lab データ概要がグリッドに一覧表示される。 ユースケース:Lab 詳細データ閲覧 Use Case: Lab ナレッジベース::WinLabDB::Lab 詳細データ閲覧(GD-UC02) Summary: Lab データの詳細データを表示する。 Actors: ゲストユーザ、既定ユーザ Preconditions: Lab データ一覧が表示されている。 Descriptions: 1.ユーザは Lab データ一覧からデータ行を指定し、@閲覧@を選択する。 2.WinLabDB は WebLabDB の Lab データ取得機能を呼び出す。 3.WebLabDB は指定されたデータ行を特定し、ユーザ Lab データベースからその行の詳 細データ(「分類」、「タイトル」、「詳細」)を取り出し、WinLabDB に返す。 4.WinLabDB は Lab データ詳細画面を開き、Lab 詳細データを表示する。 Exceptions: なし Postconditions: Lab 詳細データが表示される。 Labデータ一覧表示 input -ユーザID +入力する() Labデータ詳細表示 input output -分類 -タイトル -更新日 +グリッド表示する() -データ行 +指定する() output -分類 -タイトル -詳細 +詳細画面に表示する() ユーザ情報データベース ユーザLabデータベース -ユーザID -データファイル名 +格納する() +取り出す() -分類 -タイトル -詳細 -更新日 +格納する() +取り出す() 図 3.7 Lab データ表示に関わるクラス図 ユースケース:Lab データ登録 Use Case: Lab ナレッジベース::WinLabDB::Lab データ登録(SD-UC01) Summary: Lab データを新規に登録する。 Actors: 既定ユーザ Preconditions: Lab データ一覧が表示されている。 Descriptions: 1.ユーザは@追加@を選択する。 19 2.WinLabDB は Lab データ詳細画面を開く。 3.ユーザは Lab 詳細データ(「分類」、 「タイトル」、 「詳細」)の内容を入力し、@OK@ボ タンを押す。 4.ユーザは@サーバへ送信@する。 5.WinLabDB は WebLabDB のデータ更新機能を呼び出す。 6.WebLabDB は Lab データをユーザ Lab データベースへ格納する。 Exceptions: なし Postconditions: Lab データが登録される。 ユースケース:Lab データ編集 Use Case: Lab ナレッジベース::WinLabDB::Lab データ編集(SD-UC02) Summary: Lab データを編集する。 Actors: 既定ユーザ Preconditions: Lab データ一覧が表示されている。 Descriptions: 1.ユーザは Lab データ一覧から編集するデータ行を指定し、@編集@を選択する。 2.WinLabDB は Lab データ詳細画面を表示する。 3.ユーザは Lab 詳細データ(「分類」、 「タイトル」、 「詳細」)の内容を編集し、@OK@ボ タンを押す。 4.WinLabDB は書き換えられた Lab データを新 Lab データとして一時所持する。 5.ユーザは@サーバへ送信@する。 6.WinLabDB は WebLabDB の Lab データ更新機能を呼び出す。 7.WebLabDB は新 Lab データをユーザ Lab データベースへ格納する。 Exceptions: なし Postconditions: Lab データの内容が編集され、更新される。 Labデータ登録・編集 -分類 -タイトル -詳細 +登録する() +編集する() 図 3.8 ユーザLabデータベース -分類 -タイトル -詳細 -更新日 +格納する() +取り出す() Lab データ更新に関わるクラス図 3.3.仕様分析 仕様分析の目的は、システムの内部の構造と振る舞いをユーザの視点から規定すること である。つまりここでは何を開発するのかを定め、どのように実現するのかには触れない。 20 要求分析で具体化したシステム要求を満足することができるか検証する。 3.3.1.シナリオ分析 システムの振る舞いのモデルを作成する。シナリオを実現するオブジェクトを抽出し、 各オブジェクトが受け持つ責任範囲を明確にしながらオブジェクト間の操作の呼び出し関 係を決めていく。 (1) シナリオ記述 シナリオはユースケースの動作例を具体的に書き表すものである。ここではユースケー スを一連の流れで捉えられるように、2つのおおまかなストーリーを考え記述する。 (2) シーケンス図 シーケンス図は、ある一連の作業をする中で協調動作するオブジェクトどうしが、どの ようなメッセージをインタラクションしているのかを時系列に記述するダイアグラムであ る。横軸にオブジェクトを複数並べ、縦軸は時間の経過を示し、上から下へ向かう。各オ ブジェクトは四角い箱の中にオブジェクト名もしくは下線付きクラス名を記述し、その箱 の下から生存線と呼ばれる点線を引く。生存線はオブジェクトが生成している状態を意味 する。生存線上にある小さな長方形のアイコンは、そのオブジェクトが操作を実行してい ることを示す活性区間である。 シーケンス図では、横軸に並べられた複数のオブジェクトの間にメッセージを記述する。 メッセージは一方のオブジェクトの生存線から、もう一方のオブジェクトの生存線へ向か う矢印であらわす。なおここではオブジェクトをクライアントアプリケーション、Web サ ービス、データベースに分け、それぞれ<<WinLab>>、<<WebLab>>、<<DB>>とステレ オタイプ表示してある。 シナリオ1:ゲストユーザが詳細 Lab データを閲覧する ユーザが WinLabDB Windows アプリケーションを起動するとメイン画面が開く。ユー ザはまずメイン画面のユーザ一覧ボタンを押して、ユーザ一覧を表示させる。ユーザ一覧 の中からユーザを指定し、そのユーザの Lab データ一覧を表示させる。Lab データ一覧の 中から興味のある項目を指定して、閲覧メニューを選択する。すると詳細画面が開き、Lab データの詳細データが表示される。 21 ユーザ <<WinLab>> メイン画面 <<WinLab>> <<WinLab>> <<WinLab>> ログイン画面 詳細画面 システム <<WebLab>> システム <<DB>> <<DB>> ユーザ情報 Labデータ 画面を開く() ユーザ一覧ボタンを押す() ユーザ情報取得を要求する() ユーザID一覧を取り出す() データを返す() ユーザ一覧を表示する() ユーザを指定する() データを取得する() ユーザIDを渡し、Labデータ取得を要求する() データファイル名を取得する() データを取得する() データを返す() Labデータ一覧を表示する() データ項目を選択する() データ行を取得する() データ行を渡し、Labデータ取得を要求する() データを取得する() データを返す() 画面を開き、詳細データを表示する() データを閲覧する() OKボタンを押す() 画面を閉じる() 終了メニューを選択する() 画面を閉じる() 図 3.9 シナリオ 1 のシーケンス図 22 ユーザ <<WinLab>> メイン画面 <<WinLab>> <<WinLab>> <<WinLab>> ログイン画面 詳細画面 システム <<WebLab>> システム <<DB>> <<DB>> ユーザ情報 Labデータ 画面を開く() ログインメニューを選択する() 画面を開く() ユーザIDとパスワードを入力する() データを取得する() ユーザIDとパスワードを渡し、ユーザ認証を要求する() 調べる() 認証結果を返す() ①ログインエラーを表示する() ②ログインさせる() ユーザIDを渡し、Labデータ取得を要求する() データファイル名を取得する() データを取得する() データを返す() Labデータ一覧を表示する() データ項目を選択する() データ行を取得する() データ行を渡し、Labデータ取得を要求する() データを取得する() データを返す() 画面を開き、詳細データを表示する() データを編集する() データを取得する() サーバへ送信メニューを選択する() データを渡し、Labデータ更新を要求する() データを更新する() ログアウトメニューを選択する() ログアウトさせる() 終了メニューを選択する() 画面を閉じる() 図 3.10 シナリオ 2 のシーケンス図 23 シナリオ2:既定ユーザが Lab データを編集する ユーザが WinLabDB Windows アプリケーションを起動するとメイン画面が開く。ユー ザはまずメイン画面のログインメニューを選択する。するとログイン画面が開くので、ユ ーザ ID とパスワードを入力して OK ボタンを押す。ログインすると、Lab データ一覧が表 示される。ユーザは Lab データ一覧の中から編集したい項目を指定して、編集メニューを 選択する。すると詳細画面が開くので、そこでデータの内容を編集する。編集が終わった ら、OK ボタンを押して、詳細画面を閉じる。そして最後にサーバへ送信メニューを選択し て、Lab データをサーバへ送信し、データを更新する。 3.3.2.オブジェクト分析 シナリオ分析で得られた振る舞いのモデルをもとにシステムの静的な構造のモデルを作 成する。まずシナリオ分析で抽出したオブジェクトからクラスを抽出する。次に振る舞い のモデルの中に現れる操作と属性をクラスのメンバとして加える。最後にクラス間の関連 を加える。 (1) クラス図 クラス図はシステムの静的な構造を記述する。クラスの内部構造を記述し、関連がある 複数のクラスを関係付ける。クラス図におけるクラスは四角い箱で表現し、その中は、名 前、属性、操作の 3 つの区画で区切る。 関連はクラスとクラスを結ぶ線で記述する。継承関係は白抜きの三角形をスーパークラ ス(より一般性の高いクラス)側に記述し、サブクラスとその三角形を線で結ぶ。「部分― 全体」という集約関係を記述する場合、クラスとクラスを線で結び全体クラス側に白抜き のひし形を記述する。あるクラスが別のクラスを使用するという関係を依存関係と呼び、 依存する側から依存される側に破線の矢印を引き表現する。 図 3.11 は Lab ナレッジベースの静的構造図である。WinLabDB システムは WebLabDB システムを呼び、WebLabDB システムがデータベースにアクセスする。WinLabDB システ ムが直接データベースにアクセスすることはない。 図 3.12 は WinLabDB システムの GUI 部分を表している。主なコントロールはメイン画 面から行われ、ログイン画面、詳細画面はデータを表示したり入力したりするために使用 する。 24 ユーザ情報データベース -ユーザID -パスワード -学籍番号 -E-Mailアドレス -データファイル名 +格納する() +取り出す() ユーザLabデータベース -分類 -タイトル -詳細 -更新日 +格納する() +取り出す() データベース ユーザ情報取得 -ユーザ一覧 -データファイル名 +取得する() データ送受信 -ユーザ一覧 -データファイル名 -Lab概要データ -Lab詳細データ +返す() WebLabDBシステム Labデータ更新 -Lab概要データ -Lab詳細データ +更新する() Webサービス ユーザ認証 -ユーザID -パスワード +一致するか調べる() 画面操作 -メイン画面 -ログイン画面 -詳細画面 +開く() +閉じる() 表示 -ユーザ一覧 -ログインエラー -Lab概要データ -Lab詳細データ +メッセージ表示する() +リスト表示する() +データグリッド表示する() +表示する() WinLabDBシステム ログイン処理 -ユーザ +ログインさせる() +ログアウトさせる() 図 3.11 Labデータ取得 -Lab概要データ -Lab詳細データ +取得する() データの送受信 -ユーザID -パスワード -Lab概要データ -データ行 +取得する() +渡す() Webサービス呼び出し -ユーザ情報取得 -ユーザ認証 -Labデータ取得 -Labデータ更新 +要求する() Lab ナレッジベースのクラス図 25 WinLabDB ボタン -OK -キャンセル ボタン -OK -キャンセル ログイン画面 画面 詳細画面 テキストボックス -分類 -タイトル -詳細 テキストボックス -ユーザID -パスワード メイン画面 メニューコントロール メインメニュー -ログイン/ログアウト -名前を付けて保存 -サーバへ送信 -終了 データリスト コンテキストメニュー -編集・閲覧 -追加 -削除 図 3.12 ユーザ一覧 -ユーザID Lab概要データ一覧 -分類 -タイトル -更新日 WinLabDB の GUI をあらわすクラス図 26 第4章 Lab ナレッジベースの実装 4.1.実装環境と製品概要 4.1.1.プラットフォーム Microsoft .NET XML Web サービスとアプリケーションを構築、配置、および実行するためのプラットフ ォーム マイクロソフトは.NET 構想を「コンピューティングとインターネットを融合することで、 次世代のインターネット環境を構築するコンセプト」と定義している。 4.1.2..NET Framework .NET プラットフォームのプログラミングモデルを提供する環境。 開発者がアプリケーションのビジネスロジックコードを記述することに集中できるよう に 、 ソ フ ト ウ ェ ア を 作 成 す る 上 で 必 要 な 基 本 的 な 作 業 の 大 部 分 を 管 理 す る 。 .NET Framework は CLR(共通言語ランタイム)およびクラスライブラリを含んでいる。 4.1.3.開発ツール Microsoft Visual Studio .NET (Academic) (以下 VS .NET と略すことがある。) .NET Framework におけるアプリケーション開発ツール。開発言語ツールは Visual Basic .NET、Visual C# .NET、Visual C++ .NET から選べる。 4.1.4.Visual Studio .NET の機能概要 (1) インターネット対応アプリケーション開発 ASP.NET を活用する Web アプリケーションや.NET Framework 上で動作する Windows アプリケーションをプログラミングできる。 (2) Windows アプリケーション開発 Windows フォームコントロールの利用により、ユーザインタフェースをビジュアルにデ 27 ザインし、コンポーネントのイベントにコードを記述することで、Windows アプリケー ションが作成できる。 (3) XML Web サービス開発 XML Web サービス開発を ASP.NET によって実現する。業界標準である SOAP および WSDL に準拠している。 ASP .NET (旧 ASP (Active Server Pages)) .NET Framework アプリケーションを実行するための仕組み。 .NET Framework が提供する Web サービス用の実行エンジン。共通言語ランタイム (CLR)上で動作する。 4.2.Web サービスの実装 Microsoft Visual Studio .NET を使用して、ASP .NET による XML Web サービスおよび Windows アプリケーションを実装する。言語ツールは Visual C# .NET を使用する。なお この先の Web サービスの実装手順や説明は大澤[7]による ASP .NET による Web サービ スの実装 を参考にしている。 4.2.1.ASP.NET による Web サービスの基本 ASP .NET は IIS (Internet Information Server/Services)の拡張機能である ISAPI (Internet Server Application Program Interface)アプリケーションとして動作する。ファ イル名は aspnet_isapi.dll である。Windows 2000 Server に .NET Framework をインスト ールすると、IIS の ISAPI アプリケーションのマッピングが設定され、特定の拡張子のフ ァイルが呼び出されたときに、aspnet_isapi.dll が呼び出される。すなわち、ASP .NET が 機能するようになる。 ISAPI (Internet Server Application Program Interface) Microsoft 社が提供している、Web サーバに機能を拡張するための API。同社の Web サーバである IIS で利用できる([18] ISAPI)。 API (Application Program Interface) あるプラットフォーム(OS やミドルウェア)向けのソフトウェアを開発する際に使用 できる命令や関数の集合のこと。また、それらを利用するためのプログラム上の手続 きを定めた規約の集合。([18] API)。 28 ASP.NET で実行される拡張子のファイルは多数あるが、Web サービスの場合.asmx ファ イル、Web.config ファイル、.cs ファイルがその中心になる(表 4.1)。 表 4.1 Web サービス作成に必要なファイル Web サービスを作るのに必要なファイル ファイル名 説明 .asmx ファイル Web サービスの本体となるプログラム。ここにソースコードを記述するか、コ ンパイル済みの DLL ファイル内に存在するクラス名を記述する。あらかじめ コンパイルしておく場合、コンパイル後の DLL ファイルは仮想ディレクトリの 下の Bin サブディレクトリに配置する。 Web.config ファイル Web サービスを格納した仮想ディレクトリの設定をするためのファイル。たと えば、認証方式をどうするか、タイムアウト時間をどのようにするかなどを設 定する。このファイルは XML 形式のファイルとして構成される。 .cs ファイル C#で記述されたプログラム。Visual Studio.NET などの開発ツールによってコ ンパイルされ、コンパイル後のファイルが Bin サブディレクトリに配置される。 実行時には必要ない。 4.2.2.Web サービスのファイルを作る Web サービスのファイルの特徴は以下の 4 点である。 (1) 拡張子は.asmx プログラムは、拡張子が.asmx というファイルに記述し、IIS の適当な仮想ディレクトリ上 に配置する。 (2) ファイルの先頭に WebService ディレクティブを置く ファイルの先頭に WebService ディレクティブ<%@ WebService %>を置き、そこで言語名 とクラス名を記述する(表 4.2)。 (3) クラス宣言の前には WebService 属性を記述する クラス宣言の前には WebService 属性[WebService]を記述する。ここでは名前空間などを指 定する(表 4.3)。 名前空間(NameSpace) XML では自由に独自のマークアップ言語(タグセット)が設計できるが、情報を共 有するときに、同じタグ名が異なる要素タイプを意味して衝突してしまう可能性があ る。この問題を解決するために、名前空間はそれぞれのタグセットに固有の URI を割 り当て、名前を URI で修飾することでお互いに区別する。 29 (4) メソッドの前には WebMethod 属性を記述する メソッドの宣言の前には、WebMethod 属性[WebMethod]を記述する(表 4.4)。 表 4.2 WebService ディレクティブの属性 WebService ディレクティブの属性 属性名 解説 Class Web サービスのクラス名を指定する Codebehind Visual Studio.NET などの開発ツールがコンパイルすべき VB や C#のソースファイルを記述する。開発ツールから利用され る属性であり、ASP.NET による実行時には関与しない。 Language .asmx ファイルと同じファイルにプログラムのソースを記述する とき、その言語を指定する。 CS 、 VB 、 JS のいずれか。 表 4.3 WebService 属性のプロパティ WebService 属性のプロパティ プロパティ 解説 Description この Web サービスの説明文。この説明文は自動生成される WSDL ファイルに含まれる。 Name この Web サービス自身の公開名。 Namespace この Web サービスの名前空間の URI。 表 4.4 WebMethod 属性のプロパティ WebMethod 属性のプロパティ プロパティ 解説 Description このメソッドの説明文を記述する。この説明文は、自動生成さ れる WSDL ファイルに含まれる。 4.2.3.Web サービスの動作 Web サービスは IIS 上で実装され、ISAPI アプリケーションによって実行される(図 4.1)。 実際には、.asmx ファイルが実行されるのではなくて、それがコンパイルされてから実行さ れる。クライアントから呼び出されたときにコンパイルされ、デフォルトで、 C:¥WinNT¥Microsoft .NET¥Framework¥バージョン番号¥Temporary ASP.NET Files の下に DLL ファイルとして配置される。そして 2 回目以降はこの DLL ファイルが実行さ 30 れる。Bin サブディレクトリに保存しておくと、asmx ファイルをサーバ上に置かないで運 用することができる。 (3)実行 (1)実行 aspnet̲isapi.dll IIS (3 )実行 (4)(5 ) 結果を返す (2)読み込み Binサブディレクトリ内の コンパイル済みDLLファ イル .asmxファイル (4 )未コンパイル時は 動的にコンパイルする 図 4.1 C:¥WinNT¥Microsoft.NET¥ Framework¥バージョン番号 ¥Temporary ASP.NET Files内のコンパイル済み DLLファイル Web アプリケーションの構成([7]の図 1 から作成) 4.2.4.SOAP による呼び出し .NET Framework で SOAP を使って Web サービスを呼び出すためには、SOAP プロキ シ(SOAP メッセージの生成エンジン)を用いる。SOAP プロキシは Web サービスクラス の代理クラスのようなものなので、SOAP プロキシを使うことで、あたかも Web サービス が同じマシン上に存在するかのように利用できる。SOAP プロキシは Visual Studio.NET に付属する wsdl.exe というプログラムを使うと自動生成できる。そして用意したプログラ ムを vbc.exe を用いて作成した SOAP プロキシとともにコンパイルすると.exe ファイルが でき、これを実行すると SOAP プロキシを経由して、Web サーバ上の Web サービスが実 行される(図 4.2)。 アプリケーション Webサービス (2)SOAPによる (1)メソッドの呼び出し メソッド呼び出し (3)実行 IIS (5)結果を返す (7)結果を返す (6)SOAPによる結果 (4)読み込み .asmxファイル SOAPプロキシ 図 4.2 aspnet̲isapi.dll SOAP を使った Web サービスの呼び出し([7]図 2) 4.3. WebLabDB Web サービスの実装 WebLabDB Web サービスおよび WinLabDB Windows アプリケーションの実装にあたっ 31 ては、arton[1]による「Visual C# .NET による Web プログラミング入門」を参考にしてい る。 4.3.1.WebLabDB プロジェクト Lab データを管理するための Web アプリケーションと Web サービス。Web アプリケー ションではユーザ登録、Lab データの表示・編集などを行う。Web サービスでは Web アプ リケーションの4つの機能(ユーザ情報取得、ユーザ認証、データ取得、データ更新)を サービスとして公開する。 Web サービスを開発する場合、[ASP.NET Web サービス]としてプロジェクトを作成する。 [ASP.NET Web サービス]を選択し、プロジェクトをアップロードする Web サーバの URL (http://satoaps.sk.tsukuba.ac.jp/WebLabDB)を指定してプロジェクトを作成すると、指定 し た URL に プ ロ ジ ェ ク ト を 構 成 す る 各 種 フ ァ イ ル ( 付 録 C, 表 C-1 ) が C:¥Inetpub¥wwwroot の下に作成される。 デフォルトで Service1.asmx というファイルが作成され、Service1 クラスが実装される ので、このクラスを編集したり、必要なメソッドを追加したりしていく。ここでははじめ にファイル名を LabService.asmx と変更し、LabService クラスを実装した。WebLabDB のその他クラスは(付録 C, 表 C-2)のようになる。 LabService.asmx(付録 A-5) 1 <%@ WebService Language="c#" Codebehind="LabService.asmx.cs" 2 Class="WebLabDB.Service.LabService" %> .asmx ファイルはこのように WebService ディレクティブが記述されているだけである。 実際のソースコードは Codebehind 属性で指定されている LabService.asmx.cs ファイルに 記述される。 32 画面 4.1 VS .NET の作業画面 画面 4.1 は VS .NET の作業画面である。VS .NET は中心にデザイナ/コードビューがあ り、ここでコントロールをドラッグアンドドロップしたり、コードを生成・記述したりす る。その他にファイルの構成を示すソリューションエクスプローラやクラスビュー(画面 右上)、ファイルやコントロールのプロパティを示すプロパティウィンドウ(画面右下)、 ビルドエラーや自動変数を表示するウィンドウ(画面中下)などがある。なお画面 4.1 は LabService.asmx.cs を作成しているところである。 4.3.2.Web サービスメソッドの実装 WebLabDB Web サービスでは次の 4 つのメソッドを実装する。 (1) ユーザ情報取得 ユーザ一覧を返す。 (2) ユーザ認証 ユーザ ID とパスワードを渡すと、認証・非認証を返す。 (3) Lab データ取得 ユーザ ID を引数に指定すると、そのユーザの Lab データを返す。 (4) Lab データ更新 更新したい Lab データを渡すと、それをデータベースに更新する。 このうちユーザ情報取得と Lab データ取得のメソッドについてソースコードの内容を説 33 明していく。なお Web サービス実装の前にデータセットを作成する必要がある。しかしデ ータセットの作成は本論の主題からはずれるので付録 D に載せる。 LabService.asmx.cs(付録 A-5 から A-7) WebService 属性の記述(31-32 行) 31 32 [WebService(Namespace="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService", Description="LabData リストの表示、編集サービス") 31-32 名前空間を指定し、この Web サービスの説明を記述する。この記述は WSDL で公 開される。 ユーザ情報取得(47-60 行) 47 48 49 50 51 52 53 54 55 56 57 58 59 60 [WebMethod(Description="登録ユーザの一覧を取得する")] [SoapHeader("authentication", Required=false)] public string[] GetUsers() { Users.UserDataTable utbl = ((Users)Application["users"]).User; DataRow[] rows = utbl.Select("studentID is not null"); string[] result = new string[rows.Length]; for (int i = 0; i < rows.Length; i++) { result[i] = ((Users.UserRow)rows[i]).userID; } return result; } 47-49 [WebMethod]に続けて定義したメソッドは、Web サービスメソッドとして公開され る。 51 Application プロパティによって HttpApplicationState クラスのインスタンスにアク セスすることができるので、Global.asax で設定した users キーを利用して、ユーザ情報デ ータセットにアクセスする(詳しくは付録 D 参照)。 52 Users.xml から学籍番号が入力されているものを抽出する。 53 返送に使用する配列をデータの行の行数分確保する。 55-58 UserID 要素の内容を配列に設定する。 59 結果の文字配列を返送する。 Lab データ取得(65-80 行) 65 66 67 68 69 70 [WebMethod(Description="指定ユーザの情報の取得")] [SoapHeader("authentication", Required=false)] public LabData GetData(string userName) { LabData labData = new LabData(); Users users = (Users)Application["users"]; 34 71 72 73 74 75 76 77 78 79 80 DataRow[] rows = users.User.Select("userID='" + userName + "'"); if (rows.Length == 0) { throw new ArgumentException(Server.HtmlEncode(userName) + "が見つりませ ん”); } Utility.ReadUserFile(Server, users, (Users.UserRow)rows[0], labData); labData.AcceptChanges(); return labData; } 69 空のデータセットを用意する。 70-72 パラメータで指定されたユーザが存在するかチェックする。 74 パラメータで指定したユーザが存在しない場合、例外を通知する。 76 ユーザが存在するならば、そのユーザの Lab データが格納されているデータファイル 名をユーザ情報データベースから取得し、そのファイルを読み取る。なおこの一連の処理 は Utility クラス(付録 A-18, 36-59 行)で行われる。 77 データセットの変更を確定する。 このようにして Web サービスを実装する。 4.3.3.Web サービスの実装結果 それではできあがった Web サービスを呼び出す。ASP.NET では、直接 URL を指定して 拡張子.asmx のファイルを IIS 経由で呼び出すと、その説明文をみることができる(画面 4.2)。ここで表示される説明文は、WebService 属性や WebMethod 属性の Description プ ロパティで指定した文字列となる。 画面 4.2 Web サービスの呼び出し 35 こ の ペ ー ジ に は [ サ ー ビ ス の 説 明 ] と 実 装 し た メ ソ ッ ド ( こ の 場 合 は [GetUsers] 、 [CheckCredential]、[SetData]、[GetData]の 4 つ)というリンクがある。[サービスの説明] のリンクをクリックすると、実装した Web サービスの WSDL の記述が得られる(画面 4.3)。 そして実装したメソッドのリンクをクリックすると、テスト欄の入力フォームとともに、 「SOAP」、 「HTTP の取得」、 「HTTP ポスト」の 3 つのデータ例が表示される(画面 4.4)。 これら 3 つのデータ例は、どのような形式のデータを送信すると、どのような出力が戻っ てくるかを示している。 画面 4.3 画面 4.4 WSDL テスト画面 36 またテスト欄には、メソッドの引数を入力できる入力フィールドが用意されている(画 面 4.5)。この入力フィールドに適当な値を入力し、起動ボタンを押すと、実際にこの Web サービスの実装メソッドが呼び出され、実行結果が XML 形式で得られる(画面 4.6)。 画面 4.5 画面 4.6 テスト実行 実行結果 (1) WebLabDB Web サービスの WSDL 画面 4.3 で表示される WSDL について触れておく。WSDL は VS .NET によって自動生 成され、Web サービス自身の URL や提供するサービス(処理)の名称、利用する通信プロ トコルなどが記述してある。 WSDL(付録 B-1 から B-6) Web サービスが備える処理名(97-99 行) 97 98 99 - <message name="GetUsersSoapIn"> <part name="parameters" element="s0:GetUsers" /> </message> 使用する通信プロトコル(HTTP を指定)(199-200 行) 199 200 - <binding name="LabServiceSoap" type="s0:LabServiceSoap"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" /> Web サービス名と URL(290-294 行) 290 291 292 293 - <service name="LabService"> <documentation>LabData リストの表示、編集サービス</documentation> - <port name="LabServiceSoap" binding="s0:LabServiceSoap"> <soap:address location="http://satoaps.sk.tsukuba.ac.jp/WebLabDB/Service/LabService.asmx" /> 37 294 </port> 290 Web サービス名 293 Web サイトの URL (2) WebLabDB Web サービスの SOAP メッセージ 画面 4.4 で示されるデータ例の中で SOAP メッセージについて解説する。GetData メソ ッドの SOAP 要求サンプル(付録 B-9)を利用して、SOAP メッセージの構造を説明する (図 4.3)。 SOAPメッセージ ヘッダ POST /WebLabDB/Service/LabService.asmx HTTP/1.1 Host: satoaps.sk.tsukuba.ac.jp Content-Type: text/xml; charset=utf-8 Content-Length: length SOAPAction: "http://satoaps.sk.tsukuba.ac.jp/maeda/WebService/GetData" SOAPエンベロープ <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchemainstance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> SOAPヘッダ <soap:Header> <Authentication xmlns="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService"> <User>string</User> <Password>string</Password> </Authentication> </soap:Header> SOAP本体 <soap:Body> <GetData xmlns="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService"> <userName>string</userName> </GetData> </soap:Body> </soap:Envelope> 図 4.3 SOAP メッセージの構造 SOAP メッセージはヘッダと SOAP エンベロープからなる。ヘッダ部分では SOAP のリ クエストメッセージは HTTP の POST メソッドを使うことが記述されている。SOAP メッ セージは実際にサーバで処理されるべきメッセージドキュメントである。さらに SOAP エ ンベロープは SOAP ヘッダと SOAP 本体からなる。SOAP ヘッダには SOAP 本体をどのソ フトウェアが処理するのかという情報や、クライアントの認証データを示す情報が記載さ れている。この場合認証データは User と Password であることが示されている。SOAP 本 体には、クライアントからのリクエストメッセージとして、メソッドとパラメータを記載 38 する。GetData メソッドでは userName をパラメータとすることが示されている。 4.4.WinLabDB Windows クライアントの実装 4.1.1.WinLabDB プロジェクト WebLabDB のクライアントアプリケーションとして、Windows アプリケーションを実装 する。プロジェクト名は WinLabDB とし、付録 C, 表 C-3 のファイルを作成した。また WinLabDB におけるログイン処理を受け持つ LoginDlg プロジェクト(付録 C, 表 C-4)も 作成した。 4.4.2.SOAP クライアントの作成 Visual Studio.NET において SOAP クライアントを作成するには、[Windows アプリケ ーション]、[ASP.NET Web アプリケーション]などを作成する。そして[プロジェクト]メニ ューから[Web 参照の追加]を選択して、利用したい Web サービスへの参照を追加する。こ れは先の wsdl.exe によるプロキシを自動化するものだ。UDDI を使って検索するか、アド レス欄に WSDL のありかを示す URL を入力する(画面 4.7)。ここでは自分で作成した Web サ ー ビ ス を 参 照 す る の で 、 URL を http://satoaps.sk.tsukuba.ac.jp/WebLabDB/Service/LabService.asmx と入力し、参照を追 加する(画面 4.8)。 画面 4.7 Web 参照(UDDI 検索) 39 画面 4.8 Web 参照(URL 入力) するとプロジェクトの[Web Reference]の下に SOAP プロキシが追加され、それを経由し て Web サービスを呼び出すことができるようになる(画面 4.9)。取り込んだ Web サービ スの URL を元にネームスペースが作成される。 画面 4.9 Web 作成された Web 参照 参 照 に よ 画面 4.10 っ て 作 成 Web 参照をクラスビューで表示 さ れ る ク ラ ス は 、 System.Web.Services.Protocols.SoapHttpClientProtocol 派生クラスで、これが SOAP プ ロキシである。サーバ上に存在する Web サービスの代理としてクライアントと交渉すると いう意味である。SOAP プロキシは画面 4.10 に示されるようなメソッドを提供する。この ようにして Web 参照をプロジェクト内に作成したら、あとは通常のクラスとして利用する ことができる。 40 WinLabDBForm.cs(付録 A-20 から A-27) サービスの取得(106-120 行) 106 107 108 109 private LabService GetService() { if (labService != null) return labService; LabService svc = new LabService(); (中略) return svc; } 119 120 106 GetService()メソッドを作成する。 109 新しい LabService の SOAP プロキシを作成する。 Lab データの表示(154-167 行) 154 155 156 157 158 159 160 161 162 163 164 165 166 167 private void DisplayLabData(string userid) { LabService svc = GetService(); try { LabData data = svc.GetData(userid); labDataGrid.SetDataBinding(data.LabDataItem, ""); labDataGrid.CaptionText = userid; } catch (Exception ex) { MessageBox.Show(ex.ToString() + ":" + ex.Message); } } 159 SOAP プロキシの作成した GetData メソッドを利用し、指定ユーザの Lab データを 格納する。 160 受け取った Lab データを結合し、データグリッドに表示する。 161 キャプションにユーザ名を表示する。 データセットの送信(428-434 行) 428 429 430 431 432 433 434 private void menuItemCommit_Click(object sender, System.EventArgs e) { LabService svc = GetService(); LabData.LabDataItemDataTable tbl (LabData.LabDataItemDataTable)labDataGrid.DataSource; svc.SetData(userID, (LabData)tbl.DataSet); } = 431 データグリッド上の Lab データセットをデータテーブルに格納する。 433 SOAP プロキシの作成した SetData メソッドに、ユーザ ID とデータテーブルを返 す。 41 このようにして WinLabDB Windows アプリケーションを実装する。そして Lab ナレッ ジベース全体の実装も完成する。 4.5. Lab ナレッジベースの稼動 4.5.1.必要なシステム要件 Lab ナレッジベースを稼動するために必要なソフトウェア環境は次のとおりである。 (1) 稼動確認済みの OS: Microsoft Windows 2000 Professional, Microsoft Windows 2000 Server, Microsoft Windows XP Professional (2) Microsoft .NET Framework SDK .NET Framework SDK のダウンロードサイトは次の通り。 http://www.microsoft.com/japan/msdn/netframework/downloads/sdk.asp (3) Microsoft Internet Explore 5.0 以降 4.5.2.インストール 開発コンピュータでは、WinLabDB プロジェクトはデフォルトで、C:¥Documents and Settings¥Administrator¥My Documents¥Visual Studio Projects の下に配置される。 WinLabDB プ ロ ジ ェ ク ト デ ィ レ ク ト リ ( 画 面 4.11 ) の bin¥Debug 内 に で き た WinLabDB.exe と LoginDlg.dll(画面 4.12)を各コンピュータにインストールする。 画面 4.11 WinLabDB ディレクトリ 画面 4.12 42 bin¥Debug ディレクトリ 4.5.3.Lab ナレッジベースの実行結果 起動すると Windows フォームが表示される。これが Lab ナレッジベースのメイン画面で ある(画面 4.13)。 画面 4.13 起動時のメイン画面 ユーザ登録を行う場合はメイン画面右上のリンクボタンをクリックし、Web アプリケー ション上のユーザ登録画面から行う(画面 4.14)。 画面 4.14 ユーザ登録画面 43 メイン画面の[ユーザ一覧]をクリックすると GetUsers メソッドが呼ばれ、フォーム左側 のリストビューに登録ユーザの一覧が表示される(画面 4.15)。またユーザログイン時には そのユーザのアイコンが変わる(画面 4.16)。リストビュー上のユーザをクリックすると、 フォーム右側のデータグリッドに指定ユーザの Lab データ概要一覧が表示される(GetData メソッドの呼び出し)(画面 4.17)。 画面 4.15 ユーザ一覧リスト 画面 4.17 画面 4.16 ログインユーザを示すユーザ一覧リスト Lab データ概要表示(メイン画面) このようにユーザ一覧を表示し、ユーザを指定することでそのユーザの Lab データ概要 を閲覧することができる。もしプリンタの設定について知りたいときは、この Lab データ 概要一覧の中から該当項目を探す。項目ごとにソートできるので、分類項目でソートし、 周辺機器に関するデータを優先して探す。すると プリンタのインストール というタイ トルが見つかるので、その項目の詳細を閲覧する。 データグリッドでデータ項目を右クリックすると編集などのメニューを持ったコンテキ 44 ストメニューが表示される(画面 4.18)。コンテキストメニューから選択できるのは[編集・ 閲覧]、[削除]、[追加]である。ただし、1 行もデータが存在しない場合は[追加]のみが有効 である。 画面 4.18 データ選択とコンテキストメニュー コンテキストメニューで[編集・閲覧]もしくは[追加]を選択すると、詳細入力用のダイアロ グ(詳細画面)が表示される。 (画面 4.19)この詳細画面上でデータの閲覧や編集、登録が 行われる。 プリンタのインストール の項目を選択すると、その詳細が閲覧できる。詳細には プ リンタドライバのインストールとポートの設定・追加が必要。・・・ とあるので、これを 参考にプリンタを設定することができるだろう。 画面 4.19 詳細画面 データを更新するときには、ログインする必要がある。ログインはファイルメニュー から行う(画面 4.20)。ファイルメニューから[ログイン]を選択するとログイン画面が開く ので、そこにユーザ ID とパスワードを入力する(画面 4.21)とログインでき、そのユーザ の Lab データ概要一覧が表示される(画面 4.22)。 45 画面 4.20 画面 4.21 ファイルメニュー 画面 4.22 ログイン画面 ログインユーザによる Lab データ概要表示(メイン画面) さらにここからデータ項目を選択し、詳細画面でデータの編集もしくは登録を行ったら、 サーバへ送信してデータを更新する。ファイメニューから[サーバへ送信]を選択すると SetData メソッドが呼び出され、データが更新される(画面 4.23)。一連の作業が終了した ら、ファイルメニューからログアウトして終了する。これが Lab ナレッジベースの一連の 流れである。 画面 4.23 ログイン時のファイルメニュー 46 第5章 結論 本研究では Web サービスを利用して、研究室における知識資源を有効活用するナレッジ ベースを構築した。Web サービスという新しい Web 技術への理解と、ナレッジベースのシ ステムモデル作りが研究の中心であった。 第 1 章では Web サービスとは何か説明した。Web サービスとはインターネットプロトコ ルによってソフトウェアシステムを連携する技術であり、今標準化が進んでいるところで ある。また Web サービスを支える技術として SOAP を紹介した。メッセージの配送手順や 受信メッセージの処理手順を定めた XML 形式の SOAP メッセージをやりとりすることに よって Web サービスは実現される。 第 2 章では研究室における知識資源について分析し、知識資源のナレッジベース化を提 案した。「知識」とは「場」に応じた情報のことである。「知識」を有効活用しようとする ナレッジマネジメントについて取り上げた。 第 3 章ではナレッジベース構築のために、システムのイメージをより具体化する分析を 試みた。ユースケース分析では、システムの持つ機能とシステム境界を定義した。シナリ オ分析では、システムの振る舞いモデルを作るためにオブジェクト間のインタラクション について分析した。オブジェクト分析では、システムの静的構造を明確にするためにクラ ス属性と操作、クラス間の関連についてまとめた。 第 4 章ではナレッジベースの実装を行った。まず Web サービス部分を実装し、次にそれ を利用するクライアント部分を実装した。Web サービス部分では、Web サービスの主要フ ァイルである.asmx ファイルについて説明した。クライアント部分では、Web 参照機能に より自動生成される SOAP プロキシを利用することで Web サービスクラスが自由に使える ことを示した。 本研究を通して Web サービスへの理解を深め、Lab ナレッジベースを構築するという初 期の 2 つの目標を達成することができた。初めのうちは Web サービスの実体をつかむこと が難しく、簡単に理解することはできなかったが、技術の調査、事例の研究、システム開 発というように、技術、事例、開発を何度も繰り返すことによって、Web サービスとは何 なのか次第に染み渡ってきた気がする。 47 またナレッジベースのシステムモデルの構築は、UML というオブジェクト指向方法論を 取り入れることで論理的に進めることができた。本研究は Web サービスを UML でどのよ うに扱うのか試行錯誤するという点で新たな挑戦であったとも言える。 Web サービスは今まさに進化している技術である。本研究を進めている間にも標準化が 進み、企業での導入事例が急増している。本研究を開始した 2002 年夏には Web サービス に関する出版物はまだまだ少なかった。研究を進めていくなかで、Web 上に参考となる資 料や記事が多く掲載されるようになっていった。Web サービス普及期となった 2003 年、 Web サービスのこれからを見守りたい。そしてもし機会があったら、自らもその波の中で Web サービスの技術革新に携わっていきたいと思う。 48 謝辞 まず本研究の全般に渡りご指導頂いた佐藤亮先生に深く感謝致します。研究に関連があ ると思われる文献や資料などを惜しまず提供してくださったこと、そして何よりも自分で 考える力や人に説明する力をつけていただいたことに大変感謝しております。 また本研究の様々な段階において、システムエンジニアの立場から助言をしてくださっ た先輩方や友人たちに感謝致します。学術的視点からだけではなく、ビジネス環境を踏ま えて考察することができました。 そして研究室の仲間たちに心から感謝します。常に議論を交わし理解を深め合った井田 さん、細かい疑問や度重なる質問にも根気よく応じてくれた日向君、技術面でのサポート に加え常に客観的な視点で意見をくれた二村君、異なるテーマの研究に取り組み違う観点 から影響を与えてくれた玖島君をはじめ、様々な面でサポートして下さった研究室の皆さ ん、本当にどうもありがとうございました。 最後に、陰ながら支えはげましてくれた父と母に感謝します。 49 参考文献 [1] arton, 「Visual C# .NET による Web プログラミング入門 ―XML Web サービスと Web アプリケーション構築のノウハウ―」, アスキー, 2002/9 [2] Joseph Schmuller 著, 長瀬嘉秀監訳, 「独習 UML 改訂版」, 翔泳社, 2002/10 [3] 紺野登, 「ビジュアル ナレッジマネジメント入門」, 日経新聞社, 2002/6 [4] 野中郁次郎, 紺野登, 「知識経営のすすめ」, ちくま新書, 2000 [5] 吉田裕之, 山本里枝子, 上原忠弘, 田中達雄, 「UML によるオブジェクト指向開発 実 践ガイド」, 技術評論社, 2000/5 [6] 吉松史彰, 山崎明子, 「.NET プログラマーズライブラリシリーズ C# .NET プログラ ミングマニュアル」, 技術評論社, 2002/9 [7] 大澤文孝, ASP .NET による Web サービスの実装 , Web サービス完全ガイド, 日経 BP 社, 2002/6/30, pp.92-108 [8] 玉置亮太, 「Web サービス」を基礎から理解する , 日経 IT プロフェッショナル, 2002/4/1, 日経 BP 社, pp.68-75 [9] 矢沢久雄, Visual Studio .NET の全貌(前編) , 日経コンピュータ, No.545, 日経 BP 社, pp.150-157 [10] 鐘ヶ江弘章, 出版物注文販売ビジネスモデルの考察と実現 , 筑波大学社会工学類 平 成 12 年度卒業論文, 2001/3 [11] 西山和樹, e-旅館の実現 , 筑波大学社会工学類 平成 12 年度卒業論文, 2001/3 [12] 二村暢之, 個人対応の衣服作りのために , 筑波大学社会工学類 平成 13 年度卒業論 文, 2002/3 50 [13] 芳野浩二, カスタマイズドウェアの製造販売ビジネスモデルの構築 , 筑波大学社会 工学類 平成 13 年度卒業論文, 2002/3 [14] Microsoft .NET http://www.microsoft.com/japan/net/ [15] Microsoft Visual Studio ホームページ http://www.microsoft.com/japan/msdn/vstudio/default.asp [16] MSDN Online http://www.microsoft.com/japan/msdn/default.asp [17] World Wide Web Consortium http://www.w3.org/ [18] 情報・通信事典 e-Words http://e-words.jp [19] Microsoft .NET Framework 製品概要 , Microsoft .NET Framework ホーム http://www.microsoft.com/japan/msdn/netframework/prodinfo/overview.asp [20] Visual Basic および Visual C#のファイルの種類とファイル名の拡張子 , MSDN ラ イブラリ http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/vb con/html/vbconprojectitemsinvisualbasic.asp [21] Visual Studio における ASP .NET Web サービスプロジェクト , MSDN ライブラ リ http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/vb con/html/vbconaspnetwebserviceprojectsinvisualstudio.asp [22] Web Services Architecture Requirements , W3C Working Draft 14 November 2002 http://www.w3.org/TR/2002/WD-wsa-reqs-20021114 51 付録 A ソースコード WebLabDB プロジェクト DataList.aspx 1 2 <%@ Page language="c#" Codebehind="DataList.aspx.cs" Inherits="WebLabDB.DataList.DataList" %> AutoEventWireup="false" DataList.aspx.cs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; using System.Web; using System.Web.Security; using System.Web.SessionState; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; namespace WebLabDB.DataList { /// <summary> /// DataList の概要の説明です。 /// </summary> public class DataList : System.Web.UI.Page { protected System.Web.UI.WebControls.Label Label4; protected System.Web.UI.WebControls.DataGrid DataGrid1; protected System.Web.UI.WebControls.Button Button1; private const string GuestUser = "*guest*"; public const string UserFile = "dfFileName"; public const string DataSet = "DataSet"; public const string SelectedRecord = "SelectedRecord"; private const string SortCommand = "SortCommand"; protected System.Web.UI.WebControls.Label titleLabel; private const string FilterCommand = "FilterCommand"; private void Page_Load(object sender, System.EventArgs e) { if (Session["selectedUser"] == null) { Response.Redirect("/WebLabDB/index.aspx"); } if (IsPostBack == false) { string user = GuestUser; HttpCookie cookie = Request.Cookies[FormsAuthentication.FormsCookieName]; if (cookie != null) { A-1 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value); if (ticket != null) { user = ticket.Name; } } LabData labData = new WebLabDB.LabData(); labData.DataSetName = "LabData"; labData.Locale = new System.Globalization.CultureInfo("en-US"); labData.Namespace = "http://tempuri.org/LabData.xsd"; string selected = (string)Session["selectedUser"]; Label4.Text = selected + " (Login: " + user + ")"; Users users = (Users)Application["users"]; DataRow[] row = users.User.Select("userID='" + selected + "'"); string fname = String.Empty; if (row.Length > 0 && ((Users.UserRow)row[0]).IsdfNameNull() == false) { fname = Server.MapPath("/WebLabDB/" + ((Users.UserRow)row[0]).dfName); labData.ReadXml(fname); } else { string dFName FormsAuthentication.HashPasswordForStoringInConfigFile(selected + DateTime.Now.ToString(), "sha1") + ".xml"; fname = Server.MapPath("/WebLabDB/" + dFName); labData.WriteXml(fname); } = lock (users) { ((Users.UserRow)row[0]).dfName = dFName; row[0].AcceptChanges(); users.WriteXml(Server.MapPath("/WebLabDB/Users.xml")); } Session[UserFile] = fname; Session[DataSet] = labData; Session[SortCommand] = "recordDate ASC"; string filter = String.Empty; if (user == GuestUser || user != selected) { for (int i = 4; i < DataGrid1.Columns.Count; i++) { DataGrid1.Columns[i].Visible = false; } } } Session[FilterCommand] = filter; DataGrid1.DataSource = labData.LabDataItem.Select(filter, "recordDate ASC"); DataGrid1.DataBind(); } private void Button1_Command(object A-2 sender, 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 System.Web.UI.WebControls.CommandEventArgs e) { Response.Redirect(Response.ApplyAppPathModifier("/WebLabDB/index.aspx")); } private void DataGrid1_EditCommand(object System.Web.UI.WebControls.DataGridCommandEventArgs e) { DataGrid1.EditItemIndex = e.Item.ItemIndex; DataGrid1.DataSource = GetRows(); DataGrid1.DataBind(); } source, private void DataGrid1_CancelCommand(object System.Web.UI.WebControls.DataGridCommandEventArgs e) { DataGrid1.EditItemIndex = -1; DataGrid1.DataSource = GetRows(); DataGrid1.DataBind(); } source, private void DataGrid1_UpdateCommand(object System.Web.UI.WebControls.DataGridCommandEventArgs e) { UpdateDataItem(e.Item); DataGrid1.EditItemIndex = -1; DataGrid1.DataSource = GetRows(); DataGrid1.DataBind(); } source, private void DataGrid1_DeleteCommand(object source, System.Web.UI.WebControls.DataGridCommandEventArgs e) { LabData.LabDataItemDataTable tbl = (LabData.LabDataItemDataTable)((LabData)Session[DataSet]).LabDataItem; DataRow[] dr = GetRows(tbl); tbl.RemoveLabDataItemRow((LabData.LabDataItemRow)dr[e.Item.ItemIndex]); tbl.AcceptChanges(); SaveDataSet(); DataGrid1.EditItemIndex = -1; DataGrid1.DataSource = GetRows(tbl); DataGrid1.DataBind(); } private void DataGrid1_ItemCommand(object source, System.Web.UI.WebControls.DataGridCommandEventArgs e) { if (e.CommandName == "Insert") { LabData.LabDataItemDataTable tbl = (LabData.LabDataItemDataTable)((LabData)Session[DataSet]).LabDataItem; LabData.LabDataItemRow newRow = (LabData.LabDataItemRow)tbl.NewRow(); newRow._class= String.Empty; newRow.title = String.Empty; //newRow.detail = String.Empty; newRow.recordDate = DateTime.Today; tbl.AddLabDataItemRow(newRow); DataRow[] dr = GetRows(tbl); A-3 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 for (int i = 0; i < dr.Length; i++) { if (dr[i] == newRow) { DataGrid1.EditItemIndex = i % DataGrid1.PageSize; DataGrid1.CurrentPageIndex = i / DataGrid1.PageSize; break; } } DataGrid1.DataSource = dr; DataGrid1.DataBind(); } else if (e.CommandName == "SortrecordDate") { DataGrid1.EditItemIndex = -1; Session[SortCommand] = "recordDate DESC"; DataGrid1.DataSource = GetRows(); DataGrid1.DataBind(); } } private void DataGrid1_PageIndexChanged(object System.Web.UI.WebControls.DataGridPageChangedEventArgs e) { DataGrid1.EditItemIndex = -1; DataGrid1.CurrentPageIndex = e.NewPageIndex; DataGrid1.DataSource = GetRows(); DataGrid1.DataBind(); } source, private void DataGrid1_SelectedIndexChanged(object sender, System.EventArgs e) { if (DataGrid1.EditItemIndex >= 0) { Session[SelectedRecord] = UpdateDataItem(DataGrid1.Items[DataGrid1.EditItemIndex]); } else { DataRow[] rows = GetRows(); Session[SelectedRecord] = rows[DataGrid1.CurrentPageIndex * DataGrid1.PageSize + DataGrid1.SelectedIndex]; } Response.Redirect("/WebLabDB/DataItem.aspx"); } private DataRow[] GetRows() { return GetRows((LabData.LabDataItemDataTable)((LabData)Session[DataSet]).LabDataItem); } private DataRow[] GetRows(LabData.LabDataItemDataTable tbl) { return tbl.Select((string)Session[FilterCommand], (string)Session[SortCommand]); } A-4 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 DataRow UpdateDataItem(DataGridItem item) { DataRow[] dr = GetRows(); LabData.LabDataItemRow eRow (LabData.LabDataItemRow)dr[DataGrid1.EditItemIndex]; eRow._class = (((DropDownList)item.Cells[0].Controls[1]).SelectedItem.Value); eRow.title = (((TextBox)item.Cells[1].Controls[1]).Text); //eRow.detail = (((TextBox)item.Cells[2].Controls[1]).Text); eRow.recordDate = Convert.ToDateTime(DateTime.Today); eRow.AcceptChanges(); SaveDataSet(); return eRow; } = void SaveDataSet() { string dfName = (string)Session[UserFile]; ((LabData)Session[DataSet]).WriteXml(dfName); } private void DataGrid1_ItemDataBound(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e) { if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) { foreach (Control c in e.Item.Controls) { if (c is TableCell) { foreach (Control x in c.Controls) { if (x.ID == "Label2") { Label lb = (Label)x; lb.Text = Server.HtmlEncode(lb.Text); break; } } } } } } } } LabService.asmx 1 2 <%@ WebService Language="c#" Class="WebLabDB.Service.LabService" %> LabService.asmx.cs 1 2 3 using System; using System.Collections; using System.ComponentModel; A-5 Codebehind="LabService.asmx.cs" 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 using System.Data; using System.Diagnostics; using System.Web; using System.Web.Security; using System.Web.Services; using System.Xml; using System.Web.Services.Protocols; namespace WebLabDB.Service { /// <summary> /// SOAP ヘッダの定義 /// </summary> public class Authentication : SoapHeader { /// <summary> /// ユーザ ID /// </summary> public string User; /// <summary> /// パスワード /// </summary> public string Password; } /// <summary> /// LabService の概要の説明です。 /// </summary> [WebService(Namespace="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService", Description="LabData リストの表示、編集サービス")] public class LabService : System.Web.Services.WebService { public Authentication authentication; public LabService() { //CODEGEN: この呼び出しは、ASP.NET Web サービス デザイナで必要です。 InitializeComponent(); } /// <summary> /// ユーザ情報の取得 /// </summary> [WebMethod(Description="登録ユーザーの一覧を取得する")] [SoapHeader("authentication", Required=false)] public string[] GetUsers() { Users.UserDataTable utbl = ((Users)Application["users"]).User; DataRow[] rows = utbl.Select("studentID is not null"); string[] result = new string[rows.Length]; } for (int i = 0; i < rows.Length; i++) { result[i] = ((Users.UserRow)rows[i]).userID; } return result; /// <summary> /// 指定ユーザの情報取得 A-6 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 /// </summary> [WebMethod(Description="指定ユーザーの情報の取得")] [SoapHeader("authentication", Required=false)] public LabData GetData(string userName) { LabData labData = new LabData(); Users users = (Users)Application["users"]; DataRow[] rows = users.User.Select("userID='" + userName + "'"); if (rows.Length == 0) { throw new ArgumentException(Server.HtmlEncode(userName) + "が見つかりません "); } Utility.ReadUserFile(Server, users, (Users.UserRow)rows[0], labData); labData.AcceptChanges(); return labData; } [WebMethod(Description="指定ユーザーの Lab データの更新")] [SoapHeader("authentication", Required=true)] public void SetData(string userName, LabData data) { if (Context.User == null || Context.User.Identity.Name != userName) { throw new SoapException("認証されたユーザーではありません", SoapException.ClientFaultCode); } try { LabData labData = new LabData(); Users users = (Users)Application["users"]; DataRow[] rows = users.User.Select("userID='" + userName + "'"); if (rows.Length == 0) { throw new ArgumentException(Server.HtmlEncode(userName) + "が見つかりませ ん"); } string fname = Utility.ReadUserFile(Server, users, (Users.UserRow)rows[0], labData); labData.Clear(); labData.Merge(data); labData.WriteXml(fname); } } catch (Exception e) { throw new SoapException(e.Message, SoapException.ServerFaultCode, e); } [WebMethod(Description="認証のみを行う")] [SoapHeader("authentication", Required=true)] public bool CheckCredential() { return (Context.User != null && Context.User.Identity != null && Context.User.Identity.Name != String.Empty); } } } A-7 UserAdd.aspx 1 2 <%@ Page language="c#" Codebehind="UserAdd.aspx.cs" Inherits="WebLabDB.UserAdd.UserAdd" %> AutoEventWireup="false" UserAdd.aspx.cs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; using System.Security.Cryptography; using System.Text; using System.Web; using System.Web.Security; using System.Web.SessionState; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; namespace WebLabDB.UserAdd { /// <summary> /// UserAdd の概要の説明です。 /// </summary> public class UserAdd : System.Web.UI.Page { protected System.Web.UI.WebControls.Label Label1; protected System.Web.UI.WebControls.Label Label2; protected System.Web.UI.WebControls.Label Label3; protected System.Web.UI.WebControls.Label Label4; protected System.Web.UI.WebControls.Label Label5; protected System.Web.UI.WebControls.TextBox UserID; protected System.Web.UI.WebControls.TextBox Password1; protected System.Web.UI.WebControls.TextBox Password2; protected System.Web.UI.WebControls.TextBox StudentID; protected System.Web.UI.WebControls.Button Button1; protected System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator1; protected System.Web.UI.WebControls.CompareValidator CompareValidator1; protected System.Web.UI.WebControls.RegularExpressionValidator RegularExpressionValidator1; protected System.Web.UI.WebControls.CustomValidator DupUserValidator; protected System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator2; protected System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator3; protected System.Web.UI.WebControls.ValidationSummary ValidationSummary1; protected System.Web.UI.WebControls.Label titleLabel; protected System.Web.UI.WebControls.Label Label6; protected System.Web.UI.WebControls.TextBox EMailAddress; private void Page_Load(object sender, System.EventArgs e) { } private void DupUserValidator_ServerValidate(object source, System.Web.UI.WebControls.ServerValidateEventArgs args) { Users users = (Users)Application["users"]; A-8 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 DataRow[] us = users.User.Select("UserID='" + args.Value + "'"); args.IsValid = (us.Length <= 0); } private void Button1_Click(object sender, System.EventArgs e) { if (IsValid) { Users users = (Users)Application["users"]; Users.UserRow us = users.User.NewUserRow(); us.userID = UserID.Text; us.password = MakeHash(Password1.Text); us.studentID = StudentID.Text; us.eMailAddress = EMailAddress.Text; } lock (users) { users.User.AddUserRow(us); users.WriteXml(Server.MapPath("/WebLabDB/Users.xml")); } FormsAuthentication.SetAuthCookie(UserID.Text, false); Response.Redirect("/WebLabDB/DataList/DataList.aspx"); } public static string MakeHash(string s) { byte[] bs = Encoding.Unicode.GetBytes(s); SHA1Managed SHA1 = new SHA1Managed(); return Convert.ToBase64String(SHA1.ComputeHash(bs)); } } } authModule.cs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 using System; using System.Text; using System.Xml; using System.Xml.XPath; using System.Web; using System.IO; using System.Web.Services.Protocols; using System.Security.Principal; namespace WebLabDB { public class AuthEvent : EventArgs { private HttpContext context; private string user; private string password; private IPrincipal principal; public AuthEvent(HttpContext context, string u, string p) { this.context = context; user = u; password = p; A-9 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 } } public HttpContext Context { get { return context; } } public IPrincipal Principal { get { return principal; } } public void Authenticate() { GenericIdentity i = new GenericIdentity(user); principal = new GenericPrincipal(i, new string[0]); } public void Authenticate(string[] roles) { GenericIdentity i = new GenericIdentity(user); principal = new GenericPrincipal(i, roles); } public string User { get { return user; } } public string Password { get { return password; } } public bool HasCredentials { get { return (user != null && password != null); } } /// <summary> /// authModule の概要の説明です。 /// </summary> public class AuthModule : IHttpModule { public AuthModule() { // // TODO: コンストラクタ ロジックをここに追加してください。 // } public void Dispose() { } public void Init(System.Web.HttpApplication context) { context.AuthenticateRequest += new EventHandler(this.OnEnter); } public delegate void AuthEventHandler(object sender, AuthEvent e); private AuthEventHandler authHandler; A-10 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 public void AddOnAuthEvent(AuthEventHandler h) { authHandler += h; } public void RemoveOnAuthEvent(AuthEventHandler h) { authHandler -= h; } private void OnAuthenticate(AuthEvent e) { if (authHandler != null) { authHandler(this, e); if (e.Principal != null) { e.Context.User = e.Principal; } } } void OnEnter(Object sender, EventArgs eventArgs) { HttpApplication app = (HttpApplication)sender; HttpContext cxt = app.Context; if (cxt.Request.ServerVariables["HTTP_SOAPACTION"] == null) return; Stream stm = cxt.Request.InputStream; long pos = stm.Position; XmlDocument dom = new XmlDocument(); try { dom.Load(stm); stm.Position = pos; XmlElement header (XmlElement)dom.GetElementsByTagName("Authentication").Item(0); if (header != null) { string user = header.GetElementsByTagName("User").Item(0).InnerText; string pwd = header.GetElementsByTagName("Password").Item(0).InnerText; OnAuthenticate(new AuthEvent(cxt, user, pwd)); } } catch (Exception e) { stm.Position = pos; throw new SoapException("読み込みエラー", SoapException.ClientFaultCode, e); } } } } = DataItem.aspx 1 2 <%@ Page language="c#" Codebehind="DataItem.aspx.cs" Inherits="WebLabDB.Lab.DataItem" %> A-11 AutoEventWireup="false" DataItem.aspx.cs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; using System.Web; using System.Web.SessionState; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; namespace WebLabDB.Lab { /// <summary> /// DataItem の概要の説明です。 /// </summary> public class DataItem : System.Web.UI.Page { protected System.Web.UI.WebControls.RadioButtonList ClassRBList; protected System.Web.UI.WebControls.Label Label1; protected System.Web.UI.WebControls.Label Label2; protected System.Web.UI.WebControls.TextBox TextBox1; protected System.Web.UI.WebControls.Label Label3; protected System.Web.UI.WebControls.TextBox TextBox2; protected System.Web.UI.WebControls.Button SubmitButton; protected System.Web.UI.WebControls.Label Label4; protected System.Web.UI.WebControls.Label titleLabel; protected System.Web.UI.WebControls.Button ResetButton; private void Page_Load(object sender, System.EventArgs e) { if (IsPostBack == false) { LabData.LabDataItemRow row (LabData.LabDataItemRow)Session[DataList.DataList.SelectedRecord]; if (row == null) { Response.Redirect("/WebLabDB/DataList/DataList.aspx"); } = ClassRBList.DataTextField = row._class; TextBox1.Text = row.title; //TextBox2.Text = row.detail; //Label4.Text = row.recordDate; if (row.IsdetailNull() == false) { TextBox2.Text = row.detail; } } } private void SubmitButton_Click(object sender, System.EventArgs e) { LabData.LabDataItemRow row (LabData.LabDataItemRow)Session[DataList.DataList.SelectedRecord]; if (row == null) { A-12 = 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 Response.Redirect("/WebLabDB/DataList/DataList.aspx"); } row._class = ClassRBList.DataTextField; row.title = TextBox1.Text; row.detail = TextBox2.Text; row.recordDate = DateTime.Today; row.AcceptChanges(); string dfName = (string)Session[DataList.DataList.UserFile]; ((LabData)Session[DataList.DataList.DataSet]).WriteXml(dfName); } Response.Redirect("/WebLabDB/DataList/DataList.aspx"); private void ResetButton_Click(object sender, System.EventArgs e) { Response.Redirect("/WebLabDB/DataList/DataList.aspx"); } } } Global.asax.cs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 using System; using System.Collections; using System.ComponentModel; using System.Data; using System.IO; using System.Web; using System.Web.SessionState; namespace WebLabDB { /// <summary> /// Global の概要の説明です。 /// </summary> public class Global : System.Web.HttpApplication { private WebLabDB.Users users; public Global() { InitializeComponent(); } protected void Application_Start(Object sender, EventArgs e) { Application["users"] = users; try { users.ReadXml(Context.Server.MapPath("/WebLabDB/Users.xml"), XmlReadMode.IgnoreSchema); } catch (FileNotFoundException) { } } A-M 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 private void OnAuth(object sender, AuthEvent e) { if (e.HasCredentials) { if (Utility.Authorize((Users)Application["users"], e.User, e.Password)) { e.Authenticate(); } } } protected void Session_Start(Object sender, EventArgs e) { } protected void Application_BeginRequest(Object sender, EventArgs e) { } protected void Application_EndRequest(Object sender, EventArgs e) { } protected void Application_AuthenticateRequest(Object sender, EventArgs e) { } protected void Application_Error(Object sender, EventArgs e) { } protected void Session_End(Object sender, EventArgs e) { } protected void Application_End(Object sender, EventArgs e) { } public override void Init() { IHttpModule mod = Modules["AuthModule"]; if (mod != null) { ((AuthModule)mod).AddOnAuthEvent(new AuthModule.AuthEventHandler(OnAuth)); } } } } A-14 index.aspx 1 2 <%@ Page language="c#" Inherits="WebLabDB.Startpage" %> Codebehind="index.aspx.cs" AutoEventWireup="false" index.aspx.cs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; using System.Web; using System.Web.Security; using System.Web.SessionState; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; namespace WebLabDB { /// <summary> /// WebForm1 の概要の説明です。 /// </summary> public class Startpage : System.Web.UI.Page { protected System.Web.UI.WebControls.Label Label1; protected System.Web.UI.WebControls.Label Label2; protected System.Web.UI.WebControls.TextBox UserID; protected System.Web.UI.WebControls.TextBox Password; protected System.Web.UI.WebControls.ValidationSummary ValidationSummary1; protected System.Web.UI.WebControls.RequiredFieldValidator UserIDFillValidator; protected System.Web.UI.WebControls.RegularExpressionValidator UserIDFormatValidator; protected System.Web.UI.WebControls.CustomValidator UserPassValidator; protected System.Web.UI.WebControls.DataList DataList1; protected System.Web.UI.WebControls.Button LoginButton; protected System.Web.UI.WebControls.Label titleLabel; protected System.Web.UI.WebControls.Label Label3; protected System.Web.UI.WebControls.Label Label4; protected System.Web.UI.WebControls.Label Label5; protected System.Web.UI.WebControls.Button UserAddButton; private void Page_Load(object sender, System.EventArgs e) { Users.UserDataTable utbl = ((Users)Application["users"]).User; DataList1.DataSource = utbl; DataList1.DataBind(); } private void UserAddButton_Click(object sender, System.EventArgs e) { Response.Redirect("/WebLabDB/UserAdd/UserAdd.aspx"); } private void LoginButton_Click(object sender, System.EventArgs e) { foreach (IValidator vd in Validators) { A-15 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 vd.Validate(); if (!vd.IsValid) return; } string s = FormsAuthentication.GetRedirectUrl(UserID.Text, false); if (Request.Url.Query.Length == 0) { s = "/WebLabDB/DataList/DataList.aspx"; } FormsAuthentication.SetAuthCookie(UserID.Text, false); Session["selectedUser"] = UserID.Text; Response.Redirect(s); } private void UserPassValidator_ServerValidate(object source, System.Web.UI.WebControls.ServerValidateEventArgs args) { string pwd = WebLabDB.UserAdd.UserAdd.MakeHash(Password.Text); Users users = (Users)Application["users"]; DataRow[] urs = users.User.Select("UserID='" + UserID.Text + "' and Password='" + pwd + "'"); args.IsValid = (urs.Length ==1); } private void DataList1_ItemCommand(object source, System.Web.UI.WebControls.DataListCommandEventArgs e) { Session["selectedUser"] = ((Button)e.CommandSource).Text; Response.Redirect("/WebLabDB/DataList/DataList.aspx"); } } } LabData.xsd 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?xml version="1.0" encoding="utf-8" ?> <xs:schema id="LabData" targetNamespace="http://tempuri.org/LabData.xsd" elementFormDefault="qualified" attributeFormDefault="qualified" xmlns="http://tempuri.org/LabData.xsd" xmlns:mstns="http://tempuri.org/LabData.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <xs:element name="LabData" msdata:IsDataSet="true"> <xs:complexType> <xs:choice maxOccurs="unbounded"> <xs:element name="LabDataItem"> <xs:complexType> <xs:sequence> <xs:element name="class" type="xs:string" minOccurs="0" /> <xs:element name="title" type="xs:string" minOccurs="0" /> <xs:element name="detail" type="xs:string" minOccurs="0" /> <xs:element name="recordDate" type="xs:date" /> </xs:sequence> </xs:complexType> </xs:element> </xs:choice> </xs:complexType> </xs:element> </xs:schema> A-16 Users.xsd 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?xml version="1.0" encoding="utf-8" ?> <xs:schema id="Users" targetNamespace="http://tempuri.org/Users.xsd" elementFormDefault="qualified" attributeFormDefault="qualified" xmlns="http://tempuri.org/Users.xsd" xmlns:mstns="http://tempuri.org/Users.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <xs:element name="Users" msdata:IsDataSet="true"> <xs:complexType> <xs:choice maxOccurs="unbounded"> <xs:element name="User"> <xs:complexType> <xs:sequence> <xs:element name="userID" type="xs:string" minOccurs="0" /> <xs:element name="password" type="xs:string" minOccurs="0" /> <xs:element name="studentID" type="xs:string" minOccurs="0" /> <xs:element name="eMailAddress" type="xs:string" minOccurs="0" /> <xs:element name="dfName" type="xs:string" minOccurs="0" /> </xs:sequence> </xs:complexType> </xs:element> </xs:choice> </xs:complexType> </xs:element> </xs:schema> Utility.cs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 using System; using System.Data; using System.Text; using System.Security.Cryptography; using System.Web; using System.Web.Security; namespace WebLabDB { /// <summary> /// Utility の概要の説明です。 /// </summary> public class Utility { private Utility() { // // TODO: コンストラクタ ロジックをここに追加してください。 // } public static string MakeHash(string s) { byte[] bs = Encoding.Unicode.GetBytes(s); SHA1Managed SHA1 = new SHA1Managed(); return Convert.ToBase64String(SHA1.ComputeHash(bs)); } public static bool Authorize(Users users, string user, string passwd) A-17 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 { } string pwd = MakeHash(passwd); DataRow[] urs = users.User.Select("UserID='" + user + "' and Password='" + pwd + "'"); return (urs.Length == 1); public static string ReadUserFile(HttpServerUtility Server, Users users, Users.UserRow row, LabData labData) { string fname; if (row.IsdfNameNull() == false) { fname = Server.MapPath("/WebLabDB/" + row.dfName); labData.ReadXml(fname); } else { string dFName = FormsAuthentication.HashPasswordForStoringInConfigFile(row.userID + DateTime.Now.ToString(), "sha1") + ".xml"; fname = Server.MapPath("/WebLabDB/" + dFName); labData.WriteXml(fname); lock (users) { row.dfName = dFName; row.AcceptChanges(); users.WriteXml(Server.MapPath("/WebLabDB/Users.xml")); } } } } return fname; } Web.config 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?xml version="1.0" encoding="utf-8" ?> <configuration> <system.web> <httpModules> <add type="WebLabDB.AuthModule,WebLabDB" name="AuthModule" /> </httpModules> <!-- ダイナミック デバッグ コンパイル ASPX デバッグを有効にするには、コンパイルを debug="true" に設定します。この値を False に設 定すると、 このアプリケーションの実行時のパフォーマンスが向上します。 デバッグ シンボル (.pdb 情報) をコンパイルされたページに挿入するには、 コンパイルを debug="true" に設定します。この設定によって、実行速度の遅い大きなフ ァイルが作成されます。 デバッグ時にのみ、この値を true に設定して、 それ以外のときは、常に false に設定してください。詳細については、 ASP.NET ファイルのデバッグ ドキュメントを参照してください。 --> <compilation A-18 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 defaultLanguage="c#" debug="true" /> <!-- CUSTOM ERROR MESSAGES エラーを詳細表示 (スタック トレースを含む) するのではなく、ユーザーにとって より簡単に理解できるようなエラー表示になるように customError モード値を設定します : "オン" 常にカスタム (理解しやすい) メッセージを表示します。 "オフ" 常に詳細な ASP.NET エラー情報を表示します。 "リモートのみ" ローカル Web サーバーで実行していないユーザーに対してのみ、 常にカスタム (理解しやすい) メッセージを表示します。この設定は、セキュリティを考慮し て、アプリケーションの詳細がリモートのクライアントに 表示されないようにする推奨された設定です。 --> <customErrors mode="RemoteOnly" /> <!-- AUTHENTICATION このセクションは、アプリケーションの認証ポリシーを設定します。使用できるモードは、 "Windows"、"Forms"、 "Passport" および "None" です。 --> <authentication mode="Forms"> <forms loginUrl="index.aspx" /> </authentication> <authorization> <deny users="?" /> </authorization> <!-- アプリケーション レベルのトレース ログ アプリケーション レベルのトレースは、アプリケーション内の全ページのトレース ログ出力 を有効にします。 アプリケーション トレース ログを有効にするには、trace enabled="true" に設定します。 pageOutput="true" に設定された場合、 トレース情報が各ページの下に表示されます。それ以外の場合は、 Web アプリケーション ルートから "trace.axd" ページを参照してアプリケーション トレー スを 表示できます。 --> <trace enabled="false" requestLimit="10" pageOutput="false" traceMode="SortByTime" localOnly="true" /> <!-- セッション状態の設定 既定では、ASP .NET はクッキーを使用して、要求がどのセッションに属するかを識別しま す。 クッキーが使用できない場合は、URL にセッション識別子を入力することで、セッションを 見つけることができます。 クッキーを無効にするには、sessionState を cookieless="true" に設定してください。 --> <sessionState A-19 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 mode="InProc" stateConnectionString="tcpip=127.0.0.1:42424" sqlConnectionString="data source=127.0.0.1;user id=sa;password=" cookieless="false" timeout="20" /> <!-- GLOBALIZATION このセクションはアプリケーションのグローバリゼーション設定を行います。 --> <globalization requestEncoding="utf-8" responseEncoding="utf-8" /> </system.web> </configuration> WinLabDB プロジェクト WinLabDBForm.cs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.IO; using System.Windows.Forms; using System.Data; using WinLabDB.jp.ac.tsukuba.sk.satoaps; using System.Security.Permissions; using System.Security; using System.Net; using System.Threading; namespace WinLabDB { /// <summary> /// WebLabDB スマートクライアント /// </summary> public class WinLabDBForm : System.Windows.Forms.Form { /// <summary> /// 必要なデザイナ変数です。 /// </summary> private System.Windows.Forms.DataGrid labDataGrid; private System.Windows.Forms.DataGridTableStyle dataGridTableStyle1; private System.Windows.Forms.DataGridTextBoxColumn dataGridTextBoxColumn1; private System.Windows.Forms.DataGridTextBoxColumn dataGridTextBoxColumn2; private System.Windows.Forms.DataGridTextBoxColumn dataGridTextBoxColumn3; private System.Windows.Forms.MainMenu mainMenu1; private System.Windows.Forms.MenuItem menuItem1; private System.Windows.Forms.MenuItem menuItemLogin; private System.Windows.Forms.MenuItem menuItem2; private System.Windows.Forms.SaveFileDialog saveFileDialog; private System.Windows.Forms.ContextMenu contextMenu; private System.Windows.Forms.MenuItem ctxMenuDelete; A-20 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 private System.Windows.Forms.MenuItem ctxMenuEdit; private System.Windows.Forms.MenuItem ctxMenuInsert; private System.Windows.Forms.Button userGetButton; private System.Windows.Forms.ListView userListView; private System.Windows.Forms.ImageList imageList1; private System.Windows.Forms.MenuItem menuItemCommit; private System.Windows.Forms.MenuItem menuItemSaveAs; private System.Windows.Forms.MenuItem menuItem3; private System.Windows.Forms.MenuItem menuItemExit; private System.Windows.Forms.Label titleLabel; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label2; private System.Windows.Forms.Label label3; private System.Windows.Forms.Label label4; private System.Windows.Forms.Label label5; private System.Windows.Forms.LinkLabel linkLabel1; private System.ComponentModel.IContainer components; public WinLabDBForm() { // // Windows フォーム デザイナ サポートに必要です。 // InitializeComponent(); // // TODO: InitializeComponent 呼び出しの後に、コンストラクタ コードを追加してください。 // linkLabel1.Text = "ユーザ登録はこちら"; linkLabel1.Links.Add(0,9,"http://satoaps.sk.tsukuba.ac.jp/WebLabDB/UserAdd/UserAdd.aspx"); linkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel1_LinkClicked); this.Controls.Add(linkLabel1); } /// <summary> /// 使用されているリソースに後処理を実行します。 /// </summary> protected override void Dispose( bool disposing ) { if( disposing ) { if (components != null) { components.Dispose(); } } base.Dispose( disposing ); } /// <summary> /// アプリケーションのメイン エントリ ポイントです。 /// </summary> [STAThread] static void Main() { Application.Run(new WinLabDBForm()); } const int DefaultTimeoutValue = 10000; A-21 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 private string userID; private Authentication authHeader; private LabService labService; /// <summary> /// サービスの取得 /// ユーザ ID とパスワードが設定されていたら認証ヘッダを作成する /// 本アセンブリのロードパスから Web サービスの URL を設定する /// </summary> /// <returns></returns> private LabService GetService() { if (labService != null) return labService; LabService svc = new LabService(); svc.AuthenticationValue = authHeader; Uri uri = new Uri(AppDomain.CurrentDomain.BaseDirectory); if (uri.Host != String.Empty) { Uri old = new Uri(svc.Url); svc.Url = svc.Url.Replace(old.Host, uri.Host); } labService = svc; svc.Timeout = DefaultTimeoutValue; return svc; } private void userGetButton_Click(object sender, System.EventArgs e) { LabService svc = GetService(); IAsyncResult ar = svc.BeginGetUsers(null, null); if (ar.AsyncWaitHandle.WaitOne(DefaultTimeoutValue, true)) { string[] users = svc.EndGetUsers(ar); userListView.BeginUpdate(); userListView.Clear(); foreach (string u in users) { // ログインユーザであればアイコンを変える if (userID != null && userID == u) { userListView.Items.Add(u, 1); } else { userListView.Items.Add(u, 0); } } userListView.EndUpdate(); } else { MessageBox.Show("タイムアウトしました"); } } /// <summary> /// 指定ユーザの LabData をデータグリッドに表示する /// </summary> /// <param name="userid"></param> private void DisplayLabData(string userid) { LabService svc = GetService(); A-22 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 try { LabData data = svc.GetData(userid); labDataGrid.SetDataBinding(data.LabDataItem, ""); labDataGrid.CaptionText = userid; } } catch (Exception ex) { MessageBox.Show(ex.ToString() + ":" + ex.Message); } // ユーザの変更 private void userListView_SelectedIndexChanged(object sender, System.EventArgs e) { if (userListView.SelectedItems.Count < 1) return; DisplayLabData(userListView.SelectedItems[0].Text); } private LoginDlg.LoginForm loginDlg; /// <summary> /// 指定ユーザのアイコン(ログインしているかどうかを示す)を変更する /// </summary> /// <param name="userid">ユーザ ID</param> /// <param name="icon">0...非ログイン時のアイコン、1...ログイン時のアイコン</param> private void ChangeUserIcon(string userid, int icon) { foreach (ListViewItem item in userListView.Items) { if (item.Text == userid) { item.ImageIndex = icon; return; } } // 存在しない userListView.Items.Add(userid, icon); } /// <summary> /// 現在のログイン状況に応じて認証ヘッダを SOAP プロクシに設定する /// </summary> /// <param name="userid">ログインユーザ ID。ログアウト時は null</param> /// <param name="passwd"></param> private void ChangeAuthenticationHeader(string userid, string passwd) { if (userid == null) { authHeader = null; } else { authHeader = new jp.ac.tsukuba.sk.satoaps.Authentication(); authHeader.User = userid; authHeader.Password = passwd; } // SOAP プロクシが生成済みならば SOAP ヘッダを再設定する LabService svc = labService; if (svc != null) A-23 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 { } svc.AuthenticationValue = authHeader; } /// <summary> /// ログイン処理。認証に成功したらユーザリストにスマイリーを表示させる /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void menuItemLogin_Click(object sender, System.EventArgs e) { if (userID != null) { // ログアウト処理。アイコンを戻し、LabData が表示されていれば再表示する ChangeUserIcon(userID, 0); ChangeAuthenticationHeader(null, null); if (labDataGrid.CaptionText == userID) { DisplayLabData(userID); } } else { if (loginDlg == null) { loginDlg = new LoginDlg.LoginForm(); } // ログイン処理。アイコンをスマイリーにし、LabData を表示する if (loginDlg.ShowDialog() == DialogResult.OK) { userID = loginDlg.UserID; ChangeAuthenticationHeader(userID, loginDlg.Password); LabService svc = GetService(); if (svc.CheckCredential()) { if (userListView.Items.Count <= 0) { userListView.Items.Add(userID, 1); labDataGrid.CaptionText = userID; } else { ChangeUserIcon(userID, 1); // 現在他のユーザの LabData が表示されていれば何も行わない if (labDataGrid.CaptionText != String.Empty labDataGrid.CaptionText != userID) return; } // ログインしたユーザの LabData を取得する DisplayLabData(userID); return; } // 以下はログイン失敗時にのみ実行される MessageBox.Show("ログインエラー"); ChangeAuthenticationHeader(null, null); } } userID = null; } A-24 && 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 /// <summary> /// 終了メニュー選択時の処理 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void menuItemExit_Click(object sender, System.EventArgs e) { Close(); } /// <summary> /// メニューがポップアップされるときに呼び出される /// 無効なメニューの無効化、有効なメニューの有効化をおこなう /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void menuItem1_Popup(object sender, System.EventArgs e) { menuItemLogin.Text = (userID == null) ? "ログイン(&I)" : "ログアウト(&L)"; menuItemCommit.Enabled = (userID != null && userID == labDataGrid.CaptionText); menuItemSaveAs.Enabled = (labDataGrid.DataSource != null); } /// <summary> /// ファイルを名前を付けて保存処理 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void menuItemSaveAs_Click(object sender, System.EventArgs e) { // SaveFileDialog の FileName プロパティへのアクセス権をチェックして // アクセスできるのであれば、ユーザ名をデフォルトファイル名とする FileIOPermission perm = new FileIOPermission(PermissionState.Unrestricted); try { perm.Demand(); saveFileDialog.FileName = labDataGrid.CaptionText; } catch (SecurityException) { } if (saveFileDialog.ShowDialog() == DialogResult.OK) { using (Stream s = saveFileDialog.OpenFile()) { LabData.LabDataItemDataTable tbl = (LabData.LabDataItemDataTable)labDataGrid.DataSource; tbl.DataSet.WriteXml(s); } } } /// <summary> /// データグリッドの右クリック時のコンテキストメニュー処理 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void labDataGrid_MouseDown(object System.Windows.Forms.MouseEventArgs e) A-25 sender, 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 { if (e.Button == MouseButtons.Right && labDataGrid.CaptionText != String.Empty) { DataGrid.HitTestInfo hi = labDataGrid.HitTest(e.X, e.Y); if (hi.Row >= 0) { labDataGrid.CurrentRowIndex = hi.Row; ctxMenuEdit.Enabled = true; ctxMenuDelete.Enabled = true; } else { ctxMenuEdit.Enabled = false; ctxMenuDelete.Enabled = false; } contextMenu.Show(labDataGrid, new Point(e.X, e.Y)); } } /// <summary> /// 現在選択されている DataRow を特定する /// </summary> /// <returns></returns> private LabData.LabDataItemRow GetCurrentRow() { LabData.LabDataItemDataTable tbl = (LabData.LabDataItemDataTable)labDataGrid.DataSource; BindingManagerBase bmb = labDataGrid.BindingContext[tbl]; return (LabData.LabDataItemRow)((DataRowView)bmb.Current).Row; } /// <summary> /// コンテキストメニューの編集が呼び出されたら、ItemForm に処理を委譲 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ctxMenuEdit_Click(object sender, System.EventArgs e) { LabData.LabDataItemRow row = GetCurrentRow(); using (ItemForm ifm = new ItemForm()) { ifm.SetData(row); if (ifm.ShowDialog() == DialogResult.OK) { ifm.GetData(row); labDataGrid.Refresh(); } } } /// <summary> /// コンテキストメニューで削除が選択された場合、現在の行を削除する /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ctxMenuDelete_Click(object sender, System.EventArgs e) { ItemForm ifm = new ItemForm(); LabData.LabDataItemRow row = GetCurrentRow(); row.Delete(); A-26 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 } labDataGrid.Refresh(); /// <summary> /// コンテキストメニューで挿入が選択された場合、新規の行を追加する /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ctxMenuInsert_Click(object sender, System.EventArgs e) { LabData.LabDataItemDataTable tbl (LabData.LabDataItemDataTable)labDataGrid.DataSource; LabData.LabDataItemRow row = tbl.NewLabDataItemRow(); row._class = String.Empty; row.title = String.Empty; row.recordDate = DateTime.Today; ItemForm ifm = new ItemForm(); ifm.SetData(row); if (ifm.ShowDialog() == DialogResult.OK) { ifm.GetData(row); tbl.AddLabDataItemRow(row); labDataGrid.Refresh(); } } /// <summary> /// サーバへ現在のデータセットを送信する /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void menuItemCommit_Click(object sender, System.EventArgs e) { LabService svc = GetService(); LabData.LabDataItemDataTable tbl (LabData.LabDataItemDataTable)labDataGrid.DataSource; svc.SetData(userID, (LabData)tbl.DataSet); } private void linkLabel1_LinkClicked(object System.Windows.Forms.LinkLabelLinkClickedEventArgs e) { linkLabel1.Links[linkLabel1.Links.IndexOf(e.Link)].Visited = true; System.Diagnostics.Process.Start(e.Link.LinkData.ToString()); } } } ItemForm.cs 1 2 3 4 5 6 7 using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using WinLabDB.jp.ac.tsukuba.sk.satoaps; A-27 = = sender, 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 namespace WinLabDB { /// <summary> /// ItemForm の概要の説明です。 /// </summary> public class ItemForm : System.Windows.Forms.Form { private System.Windows.Forms.TextBox itemText; private System.Windows.Forms.TextBox noteText; private System.Windows.Forms.Button button1; private System.Windows.Forms.Button button2; private System.Windows.Forms.ComboBox classComboBox; private System.Windows.Forms.Label titleLabel; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label2; private System.Windows.Forms.Label label3; /// <summary> /// 必要なデザイナ変数です。 /// </summary> private System.ComponentModel.Container components = null; public ItemForm() { // // Windows フォーム デザイナ サポートに必要です。 // InitializeComponent(); } // // TODO: InitializeComponent 呼び出しの後に、コンストラクタ コードを追加してください。 // /// <summary> /// 使用されているリソースに後処理を実行します。 /// </summary> protected override void Dispose( bool disposing ) { if( disposing ) { if(components != null) { components.Dispose(); } } base.Dispose( disposing ); } internal void SetData(LabData.LabDataItemRow row) { classComboBox.Text = row._class; itemText.Text = row.title; if (!row.IsdetailNull()) { noteText.Text = row.detail; } } internal void GetData(LabData.LabDataItemRow row) { A-28 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 row._class = classComboBox.Text; row.recordDate = DateTime.Today; row.title = itemText.Text; if (noteText.Text == String.Empty) { row.SetdetailNull(); } else { row.detail = noteText.Text; } } } } LoginDlg プロジェクト LoginDlg.cs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; namespace LoginDlg { /// <summary> /// Login Form の概要の説明です。 /// </summary> public class LoginForm : System.Windows.Forms.Form { private System.Windows.Forms.TextBox userIDTextBox; private System.Windows.Forms.TextBox passwordTextBox; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label2; private System.Windows.Forms.Button OKButton; private System.Windows.Forms.Button cancelButton; /// <summary> /// 必要なデザイナ変数です。 /// </summary> private System.ComponentModel.Container components = null; public LoginForm() { // // Windows フォーム デザイナ サポートに必要です。 // InitializeComponent(); } // // TODO: InitializeComponent 呼び出しの後に、コンストラクタ コードを追加してください。 // /// <summary> /// 使用されているリソースに後処理を実行します。 A-29 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 /// </summary> protected override void Dispose( bool disposing ) { if( disposing ) { if (components != null) { components.Dispose(); } } base.Dispose( disposing ); } public string UserID { get { return userIDTextBox.Text; } set { userIDTextBox.Text = value; } } public string Password { get { return passwordTextBox.Text; } } } } A-30 付録 B ソースコード WSDL(サービスの説明) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 <?xml version="1.0" encoding="utf-8" ?> <definitions xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:s0="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:i0="http://tempuri.org/LabData.xsd" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" targetNamespace="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService" xmlns="http://schemas.xmlsoap.org/wsdl/"> <import namespace="http://tempuri.org/LabData.xsd" location="http://satoaps.sk.tsukuba.ac.jp/WebLabDB/Service/LabService.asmx?schema=LabData" /> - <types> <s:schema elementFormDefault="qualified" targetNamespace="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService"> <s:import namespace="http://tempuri.org/LabData.xsd" /> - <s:element name="GetUsers"> <s:complexType /> </s:element> - <s:element name="GetUsersResponse"> - <s:complexType> - <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="GetUsersResult" type="s0:ArrayOfString" /> </s:sequence> </s:complexType> </s:element> - <s:complexType name="ArrayOfString"> - <s:sequence> <s:element minOccurs="0" maxOccurs="unbounded" name="string" nillable="true" type="s:string" /> </s:sequence> </s:complexType> <s:element name="Authentication" type="s0:Authentication" /> - <s:complexType name="Authentication"> - <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="User" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="Password" type="s:string" /> </s:sequence> </s:complexType> - <s:element name="GetData"> - <s:complexType> - <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="userName" type="s:string" /> </s:sequence> </s:complexType> </s:element> - <s:element name="GetDataResponse"> - <s:complexType> - <s:sequence> - <s:element minOccurs="0" maxOccurs="1" name="GetDataResult"> - <s:complexType> - <s:sequence> <s:any namespace="http://tempuri.org/LabData.xsd" /> B-1 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 </s:sequence> </s:complexType> </s:element> </s:sequence> </s:complexType> </s:element> - <s:element name="SetData"> - <s:complexType> - <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="userName" type="s:string" /> - <s:element minOccurs="0" maxOccurs="1" name="data"> - <s:complexType> - <s:sequence> <s:any namespace="http://tempuri.org/LabData.xsd" /> </s:sequence> </s:complexType> </s:element> </s:sequence> </s:complexType> </s:element> - <s:element name="SetDataResponse"> <s:complexType /> </s:element> - <s:element name="CheckCredential"> <s:complexType /> </s:element> - <s:element name="CheckCredentialResponse"> - <s:complexType> - <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="CheckCredentialResult" type="s:boolean" /> </s:sequence> </s:complexType> </s:element> <s:element name="ArrayOfString" nillable="true" type="s0:ArrayOfString" /> - <s:element name="LabData" nillable="true"> - <s:complexType> - <s:sequence> <s:any namespace="http://tempuri.org/LabData.xsd" /> </s:sequence> </s:complexType> </s:element> </s:schema> </types> - <message name="GetUsersSoapIn"> <part name="parameters" element="s0:GetUsers" /> </message> - <message name="GetUsersSoapOut"> <part name="parameters" element="s0:GetUsersResponse" /> </message> - <message name="GetUsersAuthentication"> <part name="Authentication" element="s0:Authentication" /> </message> - <message name="GetDataSoapIn"> <part name="parameters" element="s0:GetData" /> </message> - <message name="GetDataSoapOut"> <part name="parameters" element="s0:GetDataResponse" /> </message> - <message name="GetDataAuthentication"> <part name="Authentication" element="s0:Authentication" /> B-2 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 </message> - <message name="SetDataSoapIn"> <part name="parameters" element="s0:SetData" /> </message> - <message name="SetDataSoapOut"> <part name="parameters" element="s0:SetDataResponse" /> </message> - <message name="SetDataAuthentication"> <part name="Authentication" element="s0:Authentication" /> </message> - <message name="CheckCredentialSoapIn"> <part name="parameters" element="s0:CheckCredential" /> </message> - <message name="CheckCredentialSoapOut"> <part name="parameters" element="s0:CheckCredentialResponse" /> </message> - <message name="CheckCredentialAuthentication"> <part name="Authentication" element="s0:Authentication" /> </message> <message name="GetUsersHttpGetIn" /> - <message name="GetUsersHttpGetOut"> <part name="Body" element="s0:ArrayOfString" /> </message> - <message name="GetDataHttpGetIn"> <part name="userName" type="s:string" /> </message> - <message name="GetDataHttpGetOut"> <part name="Body" element="s0:LabData" /> </message> <message name="GetUsersHttpPostIn" /> - <message name="GetUsersHttpPostOut"> <part name="Body" element="s0:ArrayOfString" /> </message> - <message name="GetDataHttpPostIn"> <part name="userName" type="s:string" /> </message> - <message name="GetDataHttpPostOut"> <part name="Body" element="s0:LabData" /> </message> - <portType name="LabServiceSoap"> - <operation name="GetUsers"> <documentation>登録ユーザーの一覧を取得する</documentation> <input message="s0:GetUsersSoapIn" /> <output message="s0:GetUsersSoapOut" /> </operation> - <operation name="GetData"> <documentation>指定ユーザーの情報の取得</documentation> <input message="s0:GetDataSoapIn" /> <output message="s0:GetDataSoapOut" /> </operation> - <operation name="SetData"> <documentation>指定ユーザーの Lab データの更新</documentation> <input message="s0:SetDataSoapIn" /> <output message="s0:SetDataSoapOut" /> </operation> - <operation name="CheckCredential"> <documentation>認証のみを行う</documentation> <input message="s0:CheckCredentialSoapIn" /> <output message="s0:CheckCredentialSoapOut" /> </operation> B-3 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 </portType> - <portType name="LabServiceHttpGet"> - <operation name="GetUsers"> <documentation>登録ユーザーの一覧を取得する</documentation> <input message="s0:GetUsersHttpGetIn" /> <output message="s0:GetUsersHttpGetOut" /> </operation> - <operation name="GetData"> <documentation>指定ユーザーの情報の取得</documentation> <input message="s0:GetDataHttpGetIn" /> <output message="s0:GetDataHttpGetOut" /> </operation> </portType> - <portType name="LabServiceHttpPost"> - <operation name="GetUsers"> <documentation>登録ユーザーの一覧を取得する</documentation> <input message="s0:GetUsersHttpPostIn" /> <output message="s0:GetUsersHttpPostOut" /> </operation> - <operation name="GetData"> <documentation>指定ユーザーの情報の取得</documentation> <input message="s0:GetDataHttpPostIn" /> <output message="s0:GetDataHttpPostOut" /> </operation> </portType> - <binding name="LabServiceSoap" type="s0:LabServiceSoap"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" /> - <operation name="GetUsers"> <soap:operation soapAction="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService/GetUsers" style="document" /> - <input> <soap:body use="literal" /> <soap:header message="s0:GetUsersAuthentication" part="Authentication" use="literal" /> </input> - <output> <soap:body use="literal" /> </output> </operation> - <operation name="GetData"> <soap:operation soapAction="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService/GetData" style="document" /> - <input> <soap:body use="literal" /> <soap:header message="s0:GetDataAuthentication" part="Authentication" use="literal" /> </input> - <output> <soap:body use="literal" /> </output> </operation> - <operation name="SetData"> <soap:operation soapAction="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService/SetData" style="document" /> - <input> <soap:body use="literal" /> <soap:header d5p1:required="true" message="s0:SetDataAuthentication" part="Authentication" use="literal" xmlns:d5p1="http://schemas.xmlsoap.org/wsdl/" /> </input> - <output> <soap:body use="literal" /> </output> B-4 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 </operation> - <operation name="CheckCredential"> <soap:operation soapAction="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService/CheckCredential" style="document" /> - <input> <soap:body use="literal" /> <soap:header d5p1:required="true" message="s0:CheckCredentialAuthentication" part="Authentication" use="literal" xmlns:d5p1="http://schemas.xmlsoap.org/wsdl/" /> </input> - <output> <soap:body use="literal" /> </output> </operation> </binding> - <binding name="LabServiceHttpGet" type="s0:LabServiceHttpGet"> <http:binding verb="GET" /> - <operation name="GetUsers"> <http:operation location="/GetUsers" /> - <input> <http:urlEncoded /> </input> - <output> <mime:mimeXml part="Body" /> </output> </operation> - <operation name="GetData"> <http:operation location="/GetData" /> - <input> <http:urlEncoded /> </input> - <output> <mime:mimeXml part="Body" /> </output> </operation> </binding> - <binding name="LabServiceHttpPost" type="s0:LabServiceHttpPost"> <http:binding verb="POST" /> - <operation name="GetUsers"> <http:operation location="/GetUsers" /> - <input> <mime:content type="application/x-www-form-urlencoded" /> </input> - <output> <mime:mimeXml part="Body" /> </output> </operation> - <operation name="GetData"> <http:operation location="/GetData" /> - <input> <mime:content type="application/x-www-form-urlencoded" /> </input> - <output> <mime:mimeXml part="Body" /> </output> </operation> </binding> - <service name="LabService"> <documentation>LabData リストの表示、編集サービス</documentation> - <port name="LabServiceSoap" binding="s0:LabServiceSoap"> <soap:address location="http://satoaps.sk.tsukuba.ac.jp/WebLabDB/Service/LabService.asmx" /> B-5 294 295 296 297 298 299 300 301 302 </port> - <port name="LabServiceHttpGet" binding="s0:LabServiceHttpGet"> <http:address location="http://satoaps.sk.tsukuba.ac.jp/WebLabDB/Service/LabService.asmx" /> </port> - <port name="LabServiceHttpPost" binding="s0:LabServiceHttpPost"> <http:address location="http://satoaps.sk.tsukuba.ac.jp/WebLabDB/Service/LabService.asmx" /> </port> </service> </definitions> GetUsers SOAP 要求サンプル POST /WebLabDB/Service/LabService.asmx HTTP/1.1 Host: satoaps.sk.tsukuba.ac.jp Content-Type: text/xml; charset=utf-8 Content-Length: length SOAPAction: "http://satoaps.sk.tsukuba.ac.jp/maeda/WebService/GetUsers" <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <Authentication xmlns="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService"> <User>string</User> <Password>string</Password> </Authentication> </soap:Header> <soap:Body> <GetUsers xmlns="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService" /> </soap:Body> </soap:Envelope> 応答サンプル HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Content-Length: length <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <GetUsersResponse xmlns="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService"> <GetUsersResult> <string>string</string> <string>string</string> </GetUsersResult> </GetUsersResponse> </soap:Body> </soap:Envelope> B-6 HTTP 取得 要求サンプル GET /WebLabDB/Service/LabService.asmx/GetUsers? HTTP/1.1 Host: satoaps.sk.tsukuba.ac.jp 応答サンプル HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Content-Length: length <?xml version="1.0" encoding="utf-8"?> <ArrayOfString xmlns="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService"> <string>string</string> <string>string</string> </ArrayOfString> HTTP ポスト 要求サンプル POST /WebLabDB/Service/LabService.asmx/GetUsers HTTP/1.1 Host: satoaps.sk.tsukuba.ac.jp Content-Type: application/x-www-form-urlencoded Content-Length: length 応答サンプル HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Content-Length: length <?xml version="1.0" encoding="utf-8"?> <ArrayOfString xmlns="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService"> <string>string</string> <string>string</string> </ArrayOfString> CheckCredential SOAP 要求サンプル POST /WebLabDB/Service/LabService.asmx HTTP/1.1 Host: satoaps.sk.tsukuba.ac.jp Content-Type: text/xml; charset=utf-8 Content-Length: length SOAPAction: "http://satoaps.sk.tsukuba.ac.jp/maeda/WebService/CheckCredential" <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> B-7 <Authentication xmlns="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService"> <User>string</User> <Password>string</Password> </Authentication> </soap:Header> <soap:Body> <CheckCredential xmlns="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService" /> </soap:Body> </soap:Envelope> 応答サンプル HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Content-Length: length <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <CheckCredentialResponse xmlns="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService"> <CheckCredentialResult>boolean</CheckCredentialResult> </CheckCredentialResponse> </soap:Body> </soap:Envelope> SetData SOAP 要求サンプル POST /WebLabDB/Service/LabService.asmx HTTP/1.1 Host: satoaps.sk.tsukuba.ac.jp Content-Type: text/xml; charset=utf-8 Content-Length: length SOAPAction: "http://satoaps.sk.tsukuba.ac.jp/maeda/WebService/SetData" <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <Authentication xmlns="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService"> <User>string</User> <Password>string</Password> </Authentication> </soap:Header> <soap:Body> <SetData xmlns="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService"> <userName>string</userName> <data>dataset</data> </SetData> </soap:Body> </soap:Envelope> B-8 応答サンプル HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Content-Length: length <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <SetDataResponse xmlns="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService" /> </soap:Body> </soap:Envelope> GetData SOAP 要求サンプル POST /WebLabDB/Service/LabService.asmx HTTP/1.1 Host: satoaps.sk.tsukuba.ac.jp Content-Type: text/xml; charset=utf-8 Content-Length: length SOAPAction: "http://satoaps.sk.tsukuba.ac.jp/maeda/WebService/GetData" <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <Authentication xmlns="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService"> <User>string</User> <Password>string</Password> </Authentication> </soap:Header> <soap:Body> <GetData xmlns="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService"> <userName>string</userName> </GetData> </soap:Body> </soap:Envelope> 応答サンプル HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Content-Length: length <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <GetDataResponse xmlns="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService"> <GetDataResult>dataset</GetDataResult> </GetDataResponse> B-9 </soap:Body> </soap:Envelope> HTTP の取得 要求サンプル GET /WebLabDB/Service/LabService.asmx/GetData?userName=string HTTP/1.1 Host: satoaps.sk.tsukuba.ac.jp 応答サンプル HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Content-Length: length <?xml version="1.0" encoding="utf-8"?> <LabData xmlns="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService">dataset</LabData> HTTP ポスト 要求サンプル POST /WebLabDB/Service/LabService.asmx/GetData HTTP/1.1 Host: satoaps.sk.tsukuba.ac.jp Content-Type: application/x-www-form-urlencoded Content-Length: length userName=string 応答サンプル HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Content-Length: length <?xml version="1.0" encoding="utf-8"?> <LabData xmlns="http://satoaps.sk.tsukuba.ac.jp/maeda/WebService">dataset</LabData> B-10 付録 C 表 C-1 図表 Visual Studio .NET におけるプロジェクトファイル Visual Studio .NET におけるプロジェクトファイル 作成されるファイル 説明 WebForm1.aspx この2つのファイルによって1つの Web フォームページが構成される。.aspx ファイルに WebForm1.aspx.cs は、HTML 要素や Web フォームコントロールなど、Web フォームページのビジュアルな 要素が含まれる。WebForm1.aspx.cs クラスファイルは、WebForm1.aspx に従属する隠 しファイルである。このファイルには、イベントハンドラコードなどを含む Web フォーム ページの分離コードクラスが含まれている。 Service1.asmx この2つのファイルによって1つの XML Web サービスが構成される。Service1.asmx Service1.asmx.cs ファイルには、WebService 処理ディレクティブが含まれ、XML Web サービスの指定 可能なエントリ ポイントとして機能する。Service1.asmx.cs クラスファイルは、 Service1.asmx に従属する隠しファイルであり、このファイルには、XML Web サービス の分離コードクラスが含まれている。Service1.asmx のコードビューを表示すると、こ のファイルの内容が表示される。 AssemblyInfo.cs プロジェクト内のアセンブリに関するメタデータを含むオプションのプロジェクト情報フ ァイル。メタデータには、名前、バージョン、カルチャ情報などが含まれる。 Web.config プロジェクトで使用されている、個別の固有な URL リソース上の構成データや、 ASP.NET リソースの構成情報を含む XML ベースのファイル。 Global.asax アプリケーション レベルのイベントを処理するためのオプション ファイル。このファイ Global.asax.cs ルは、ASP.NET アプリケーションのルート ディレクトリに格納されている。このファイ ルは、実行時に解析されコンパイルされる。 .vsdisco ASP.NET の動的な XML Web サービスの探索プロセスで使用される XML ベースのフ ァイル。Web サーバ上で検索可能なパスを識別するために使用する。 .xsd 表 C-2 XML ドキュメントのスキーマを作成するためのファイル WebLabDB プロジェクトファイル WebLabDB プロジェクトファイル一覧 ファイル 種類 説明 Web フォーム Lab データ表示画面。指定ユーザの Lab データの表示と編 DataList フォルダ DataList.aspx 集を行う。 Web.config 構成ファイル C-1 Service フォルダ LabService.asmx Web サービス Web.config 構成ファイル Lab データの表示・編集サービス UserAdd フォルダ UserAdd.aspx Web フォーム Web.config 構成ファイル AssemblyInfo.cs クラス authModule.cs クラス Web サービスでユーザ認証を行うためのモジュール。 DataItem.aspx Web フォーム Lab データの詳細表示画面。Lab データの詳細情報の表示 ユーザ登録画面。ユーザの新規登録を行う。 と編集を行う。 Global.asax asax ファイル index.aspx Web ページ システムのメイン画面。ユーザ一覧を表示し、参照、ログイ ン、登録を受け付ける。 LabData.xsd XML スキーマ Lab データのデータセット Users.xsd XML スキーマ ユーザ情報のデータセット Utility.cs クラス パスワードやファイル名をハッシュ化する。 Web.config 構成ファイル WebLabDB.vsdisco 探索ドキュメント 表 C-3 WinLabDB プロジェクトファイル WinLabDB プロジェクトファイル一覧 ファイル 種類 説明 app.config 構成ファイル App.ico アイコンファイル AssemblyInfo.cs クラス ItemForm.cs Windows フォーム 詳細画面。Lab データの詳細表示と編集を行う。 WinLabDBForm.cs Windows フォーム システムのメイン画面。ユーザ一覧、Lab データを表示して、他 のあらゆる処理を行う。 表 C-4 LoginDlg プロジェクトファイル LoginDlg プロジェクトファイル一覧 ファイル 種類 App.ico アイコンファイル AssemblyInfo.cs クラス LoginDlg.cs Windows フォーム 説明 ログイン画面。ユーザ ID とパスワードの入力を行う。 C-2 付録 D データセットの作成 ADO.NET とデータセット ADO.NET は.NET Framework 内のプログラムに対してデータアクセスサービスを提供 するコンポーネント群である。WebLabDB ではユーザ情報や Lab データを格納したり検索 したりするために、ADO.NET を使用し XML をデータベースとして扱うことにする。 ADO.NET には XSD(XML Schema Definition)を使用することで、型付けされたアクセ スを提供するラッパオブジェクト(元のオブジェクトをラッピングして操作性を向上させ たオブジェクト)を自動生成する機能があるので、プログラム内のキャストが不要になり、 安全なデータ操作の記述が可能になる。 WebLabDB プロジェクトではユーザ情報を格納するデータセットと Lab データを格納す るデータセットと2つのデータセットを作成する。 表 D-1 ユーザ情報ファイルのレコード Users.xsd 表 D-2 Lab データファイルのレコード LabData.xsd 要素名 型 内容 要素名 型 内容 userID string ユーザ ID class string 分類 Password string パスワード title string タイトル studentID string 学籍番号 detail string 詳細 eMailAddress string E-Mail アドレス recordDate date dfName string データファイル名 更新日 画面 D-1 のように XML デザイナで element(要素)と型を定義すると、Users.xsd のよ うな XML スキーマが作成される。 D-1 画面 D-1 XML スキーマの作成画面 Users.xsd(付録 A-17) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?xml version="1.0" encoding="utf-8" ?> <xs:schema id="Users" targetNamespace="http://tempuri.org/Users.xsd" elementFormDefault="qualified" attributeFormDefault="qualified" xmlns="http://tempuri.org/Users.xsd" xmlns:mstns="http://tempuri.org/Users.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <xs:element name="Users" msdata:IsDataSet="true"> <xs:complexType> <xs:choice maxOccurs="unbounded"> <xs:element name="User"> <xs:complexType> <xs:sequence> <xs:element name="userID" type="xs:string" minOccurs="0" /> <xs:element name="password" type="xs:string" minOccurs="0" /> <xs:element name="studentID" type="xs:string" minOccurs="0" /> <xs:element name="eMailAddress" type="xs:string" minOccurs="0" /> <xs:element name="dfName" type="xs:string" minOccurs="0" /> </xs:sequence> </xs:complexType> </xs:element> </xs:choice> </xs:complexType> </xs:element> </xs:schema> 1 XML 宣言 2-6 ネームスペースの宣言 以下の XML の要素名や属性名で xs:というのが付加されてい るものが、XSD によって規定された名前だということはここで指定される。 7 element(要素)の定義 13-17 内包する要素の定義 D-2 13 userID という要素名の要素の型は string 型で、minOccurs(最小繰り返し数)は 0 す なわち省略可能である。 これでユーザ情報のデータセットの定義が完了し、データセットをプログラムから使え るようになる。次に作成したデータセットをプログラムでどのように使うか考え、Web ア プリケーション起動時にユーザ情報のデータセットを作成し、HttpApplicationState クラ スで管理するようにする。この設定は Global.asax にデータセットを追加し 23-34 行を追加 することで行う。なおユーザ情報ファイルの名称は Users.xml とする。 Global.asax(23-34 行)(付録 A-M) 23 24 25 26 27 28 29 30 31 32 33 34 protected void Application_Start(Object sender, EventArgs e) { Application["users"] = users; try { users.ReadXml(Context.Server.MapPath("/WebLabDB/Users.xml"), XmlReadMode.IgnoreSchema); } catch (FileNotFoundException) { } } 25 HttpApplicationContext クラスのインスタンスに Application_Start イベントの処理 を行う HttpApplication が持つデータセットのインスタンスを設定する。 26 最初の時点では Users.xml が存在しないため、try を使用して例外を捕捉して無視する ようにしておく。 28 users は、データセットの追加によって Global.asax 内に作成された Users データセッ トのフィールド名である。呼び出している ReadXml メソッドは既存の XML をデータセッ トへ読み込む処理である。第 2 パラメータの XmlReadMode.IgnoreSchema は、XML イン スタンス内で指定されているスキーマを無視して、データセットの規定情報を利用するこ とを指定する。 このようにしてデータセットへユーザ情報をロードすることができるようになる。しか しこのままでは Users.xml が存在しないので、次に UserAdd.aspx でユーザ登録処理を実 装する。ここでは Users.xml ファイルへの書き出しについてのみ説明する。 UserAdd.aspx.cs(57-76 行)(付録 A-9) テキストボックスに入力したユーザ情報を、送信ボタン(Button1)をクリックすることに よって、Users.xml ファイルへの書き出す処理である。 D-3 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 private void Button1_Click(object sender, System.EventArgs e) { if (IsValid) { Users users = (Users)Application["users"]; Users.UserRow us = users.User.NewUserRow(); us.userID = UserID.Text; us.password = MakeHash(Password1.Text); us.studentID = StudentID.Text; us.eMailAddress = EMailAddress.Text; lock (users) { users.User.AddUserRow(us); users.WriteXml(Server.MapPath("/WebLabDB/Users.xml")); } FormsAuthentication.SetAuthCookie(UserID.Text, false); Response.Redirect("/WebLabDB/DataList/DataList.aspx"); } } 61 ユーザ情報データセットを取得する。 62 新しい空の行(UserRow 型)を作成する。 63-66 UserRow のカラムに入力されたデータを格納していく。 64 安全にパスワードをファイルに格納するために、ハッシュを求める。 68-72 データセットで同期をとって、作成した行を追加し、ファイルへ書き出す。 最後に Users.xml ファイルに書き込み権限を与え、読み取りを禁止する設定を行う。 (1) ディレクトリに書き込み権限を設定 ASP.NET はデフォルトでこのファイル(Users.xml)への書き込み権限を持っていない ので、書き込み権限を付与する必要がある。よって Users.xml を配置している WebLabDB ディレクトリ(C:¥inetpub¥wwwroot¥WebLabDB)を選択して、aspnet_wp アカウント に書き込み権限を付与する。 (2) ファイルの読み取り禁止設定 さらにこのファイルは、URL に http://ホスト名/WebLabDB/Users.xml と指定すると読 み取りができてしまうので、読み取りを禁止するように設定する。IIS の管理コンソールか らファイルを選択し、読み取りチェックをオフにする。ここでの設定はあくまでも IIS の読 み取り設定であるので、Web アプリケーションのようなプログラムが実行する読み取り処 理には影響しない。 これでデータセットの作成は完了である。 D-4
© Copyright 2024 Paperzz