秋月 巌 AKIZUKI,Iwao
| HTTPクライアントからデータベースアクセス |
|---|
本連載のタイトルは「実践,データベースソリューション」なのだが,なかなか実践でつかえるようなデータベースが完成しない.しかし今回紹介するサンプルは,最低限のレベルとはいえ実践にもつかえるレベルに到達している.というのは今回はデータベースサーバー側にActive Server PagesとADOを流用したからである.自作のHTTPクライアントからActive Server Pagesを利用することで,Visual Basicアプリケーションをデータベースクライアントとして利用できる.
しかし,この方法はこの連載の本命ではない.本命はやはり「自作したデータベースサーバー」である.
前回はWebサーバーの他にデータベースサーバーType1を紹介した.今回紹介するサンプルは,HTTPクライアントだけだが,データベースサーバーType2,Type3,Type4はすでに完成している.解説は次号以降になるが,今回解説するぶんを含めて秋月巌ソリューション事務所のホームページ(http://akizuki.adsp.or.jp/)からダウンロードが可能である.
表1にそれぞれの特徴を記しておく.
| 特徴 | 短所 | 長所 | |
|---|---|---|---|
| HTTPクライアントwith ASP(今月号) | Visual Basicアプリケーションから,Active Server Pagesのデータベースアクセス機能を利用する | 仮想的なセッションしか継続できない | マルチユーザーがシングルプロセスで非同期で利用可能 |
| Type1(5,6月号) | データ転送にバリアント配列を使用 | 転送データが増えたときに極端に遅くなる | クライアントのプログラミングが容易 |
| Type2 | データ転送に文字列を使用 | 転送データが増えたときに遅くなる | サーバーのプログラミングが容易 |
| Type3 | データ転送に文字列を使用し,データを分割して転送 | ||
| Type4 | ActiveX.exeを利用し,マルチプロセス,マルチスレッドで動作 | マルチユーザー時にメモリの消費が激しい | マルチユーザーが非同期で利用可能 |
| 安いMicrosoft製品は使える!! |
|---|
あまり表立っては表明してはいないが,本連載が趣旨としているのは,「Microsoftのテクノロジーを活用しよう! でも,Microsoft製品にお金を使うのはやめよう」である.これには十分な理由がある.Internet Explorer 4.0を除いて,Microsoftが無償で提供する製品は質が高い.その理由として次のような図式がある.「無償の製品」ゆえに「開発費が膨大にはかけられない」ゆえに「シンプルに作成する」だから「ちゃんと動く」しかし「戦略製品なので手はぬけない」.
JetデータベースエンジンはMicrosoftの戦略製品である.もっとも戦略の本来の目的はxBaseつぶしだったのだが,すでに日本ではその目的は完遂している.だからVisual FoxProが日本で発売されないのだ.もちろん,現在の最大の戦略製品はInternet ExplorerとActive Server Pagesである.現時点でのInternet Explorer 4.0はインストーラが悲惨だが,これはWindows 98においては改善されるだろう.
| デファクトスタンダード化に参加 |
|---|
ところで,私はInternet Explorer 2.0以来,Internet Explorerを使用している.Netscape Navigatorはテスト目的にのみ使用する.これは,ブラウザを使用し始めた当時,Internet Explorerが無償だったからである.DDJJ誌97年11月号で細川達巳氏が連載のはじめの部分で「Internet Explorerはフリーソフトではない.それを使うことはデファクトスタンダード決定に参加しているということを自覚してほしい.組織,制度,社会を構成しているのは個々の人間なのである」と記している.
おそらく私は,一般的なユーザー以上に積極的にInternet Explorerのデファクトスタンダード化に参加していることになる.記事の執筆時にブラウザの画面ショットを使うときは特別な場合を除いてInternet Explorerを使う.マスメディアは無意識のうちにユーザーの心理を形成するはずだし,Internet Explorerユーザーを対象にしたVBSの単行本を執筆したこともある.また私は積極的にWindowsのデファクトスタンダード化に参加してきたということができる.それはWindowsがNEXTSTEPよりも,あるいはInternet ExplorerがNetscape Navigatorよりも優れた製品だから,それらを選択したわけではない.単にビジネスとしてマーケットを重視した結果の選択である.その判断は正しかったのだということができるだろう.少なくとも私が顧客に進めた方法がスタンダードの転換に取り残されたことはない.
しかし,そのような判断が長期的な視点にたって正しいものであるかはわからない.Microsoftの無償製品を利用するという私の提案は,直接Microsoftの利益につながらないとしても,明らかにMicrosoftのテクノロジーのデファクトスタンダード化を支援していることになる.それが長期的な視点にとってユーザーの利益につながるか否かは,Microsoftのささやかな良心にかかっていることになる.これは極端にというわけではないが,十分に危険な賭けである.
| HTTPクライアントの作成 |
|---|
図1:EasySocketコントロールを使用したHTTPクライアント![]() |
さて,前回はEasySocketコントロールを使用してWebサーバーを作成したので,今回はHTTPクライアントである.WebクライアントではなくHTTPクライアントと呼ぶのは,WebサーバーからHTMLファイルを受信することはできるが,その表示機能がないからである.このクライアントはWebブラウジングが目的なのではなく,データベースクライアントを目的としているので,HTMLファイルをビジュアルに表示する必要はないのである.
前回までVisual Basic付属のWinsockコントロールを使用せずにサードパーティー製のEasySocketコントロールを使用してきたのは,Winsockコントロールをサーバー側で使用した場合にマルチセッションに対応できないからである.実験レベルならばWinsockコントロールをコントロール配列として動的に動作する方法があるが,やはり,実用を目指すシステムとしては避けたい.だが今回はクライアントアプリケーションの作成なので,マルチセッションに対応する必要はない.
そこで,両方のコントロールを使用する方法を別々に解説する.クライアントアプリケーションを開発するにしてもEasySocketコントロールの方が障害も少なく使いやすいが,WinsockコントロールはVisual Basicに付属しているという利点と,加えて軽量だというメリットがある.
図1,図2が作成したHTTPクライアントの実行画面である.上のテキストボックスにサーバー名を指定し,下のテキストボックスには取得するファイル名を入力する.ファイルがフォルダの下にある場合は“/”(スラッシュ)で区切って指定する.「HTMLを取得」ボタンをクリックすると,指定したファイルの内容が表示される.図1,図2ではデータベースにアクセスせずに,単にファイルの内容を表示している.
| EasySocketコントロールを利用したHTTPクライアント |
|---|
まずEasySocketコントロールを使用したほうから説明する(リスト1).図1がHTTPクライアントの実行画面である.EasySocketコントロールには表2のようなプロパティが設定されている.80番のリモートポートプロパティは,Openメソッドの実行時に相手サーバーのHTTPサーバーに接続することを指示する.
Private Sub buttonGetHtml_Click()
Dim sendData As String
KnSocket1.RemoteHostName = textServer
KnSocket1.Open
sendData = "GET /" & textUrl.Text & " " & vbCrLf & vbCrLf
sendData = StrConv(sendData, vbFromUnicode)
Dim sendDataLen As Long
sendDataLen = LenB(sendData)
KnSocket1.Send sendData, sendDataLen
End Sub
Private Sub Form_Load()
textUrl.Text = "index.htm"
End Sub
Private Sub KnSocket1_ReceiveText(ByVal nIndex As Integer, ByVal pszText As String)
textHtml.Text = textHtml.Text + pszText
End Sub
|
| プロパティ | 値 |
|---|---|
| SocketType | 0-KnSocketSREAM |
| SocketPosition | 0-KnSocketCLIENT |
| RemotePort | 80 |
リクエストの発行
それではリスト1を順に解説してゆこう.
リクエストの発行は簡単である.以下のコードはボタンのClickイベントに記述されている.
KnSocket1.Open sendData = "GET /" & textUrl.Text & " " _ & vbCrLf & vbCrLf sendData = StrConv(sendData, vbFromUnicode) sendDataLen = LenB(sendData) KnSocket1.Send sendData, sendDataLen
Socketをオープンして,取得を希望するファイル名をGETメソッドの引数として指定してサーバーに送信する.Visual Basicは文字列をUNICODEとして扱うので,送信時にテキストを変換する必要がある.EasySocketコントロールの最初のパラメータにこの文字列を指定し,2番目のパラメータには送信するデータ長を指定する.
結果の受け取り
送信が完了すれば,後はサーバーが結果を返信するのを待つだけである.前回の自作したWebサーバーを思い出してほしい.WebサーバーはGETメソッドを受け取ると,指定されたファイルの中身をロードして内容をクライアントに返信する.それをクライアントが受け取れば,HTTPの1セッションは終了する.
結果はReceiveTextイベントプロシージャの引数pszTextによって受信する.次のようにReceiveTextイベントプロシージャには1行のコードが記述されているだけである.
Private Sub KnSocket1_ReceiveText _
(ByVal nIndex As Integer,
ByVal pszText As String)
textHtml.Text = textHtml.Text + pszText
End Sub
引数pszTextの内容をテキストボックスに出力することで,受信したデータが表示される.
サーバーはデータを送信し終わると接続を切断し,HTTPのセッションが終了する.
| Visual Basic付属のWinsockコントロールを利用したHTTPクライアント |
|---|
次に,図2はVisual Basic付属のWinsockコントロールを使用して同等の機能を提供するサンプルである(リスト2).Winsockコントロールは実行時にコントロールが表示されないので,それが外見上の違いになっている.実行時には表示されるこのコントロールのRemotePortプロパティには,HTTPのポート番号80を指定している.
Private Sub cmdSend_Click()
cmdSend.Enabled = False
txt_data.Text = ""
Winsock1.RemoteHost = txt_host.Text
Winsock1.Connect
End Sub
Private Sub Winsock1_Connect()
Winsock1.SendData "GET /" & txt_param.Text & vbCrLf & vbCrLf
End Sub
Private Sub Winsock1_Close()
Winsock1.Close
cmdSend.Enabled = True
End Sub
Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
Dim strData As String
Winsock1.GetData strData, vbString
txt_data.Text = txt_data.Text & strData
End Sub
|
図2:Visual BasicのWinsockコントロールを利用したHTTPクライアント![]() |
Winsockコントロールを使ったデータ送信
EasySocketコントロールでは,データ送信のためのSendメソッドをボタンのClickイベントに記述したが,WinsockコントロールではボタンのClickイベントでは接続処理しか行なっていない.以下のプロシージャはボタンのクリック時にボタンを無効にしてからサーバーを指定して接続を行なう.
Private Sub cmdSend_Click()
cmdSend.Enabled = False
txt_data.Text = ""
Winsock1.RemoteHost = txt_host.Text
Winsock1.Connect
End Sub
Connectメソッドの実行によって接続が開始される.接続が完了するとWinsockコントロールのConnectイベントが発生する.データの送信を行なうのは,Connectイベントプロシージャ内である.
Private Sub Winsock1_Connect()
Winsock1.SendData "GET /" & _
txt_param.Text & vbCrLf & vbCrLf
End Sub
b
接続の完了をイベントとして通知するのは,接続のための処理とアプリケーションの実行を非同期に行なうためである.接続を開始してから終了するまでの待機時間に,Visual Basicは別の処理を行なうことが可能になる.このようなノンブロッキング処理は,クライアントアプリケーションよりもサーバーアプリケーションを作成する場合に重要になる.もっとも,サーバーアプリケーションはマルチスレッドアプリケーションとして実装されることが多いので,接続の待機中に別スレッドで処理を実行すればノンブロッキング処理を行なう必要はない.
データの受信
サーバーが応答すると, DataArrivalイベントが通知される.応答の内容を取得するにはGetDataメソッドを使用する.Getdataメソッドの実行後,最初のパラメータに受信内容が格納される.
Private Sub Winsock1_DataArrival _
(ByVal bytesTotal As Long)
Dim strData As String
Winsock1.GetData strData, vbString
txt_data.Text = txt_data.Text & strData
End Sub
接続の終了
サーバー側が接続を切断するとクライアント側ではCloseイベントが発生する.接続を完全に切断するためにCloseイベントプロシージャ内で,Closeメソッドを実行する.
Private Sub Winsock1_Close()
Winsock1.Close
cmdSend.Enabled = True
End Sub
ここでSocketのクローズをしなくても,データの受信は正常に行なわれる.しかし,Socketの再オープンはできない.HTTPのようにセッションの接続と切断を繰り返すプロトコルを使用する上では必須の処理となる.
| HTTPクライアントをデータベースクライアントとして利用する |
|---|
図3:Active Server Pagesの動作原理![]() |
さて,これでHTTPクライアントが完成した.Webサーバーにリクエストを実行し,応答を受信することができる.それならば,もしWebサーバーがデータベースにアクセス可能ならば,HTTPクライアントからWebサーバーをデータベースアクセスのリクエストブローカーとして利用できることになる.
Active Server Pagesは,Webブラウザからデータベースへのアクセスを実現するモジュールである.その動作原理は,Webサーバーがブラウザの代行をしてデータベースにアクセスする形になる(図3).ならば,当然,自作HTTPクライアントからの利用も可能なはずである.
図4はHTTPクライアントからActive Server Pagesを使ってデータアクセスした様子である.Webクライアントはサーバーマシン(bogota)のHTTPホームディレクトリにあるaspdb.aspファイルをリクエストする.データベースにアクセスするのは,クライアントではなくActive Server Pagesの仕事である.
aspdb.aspの全ソースがリスト3である.普通,Active Server PagesはHTMLを動的に生成する目的で使われるが,ここでは一切HTMLを使用せずに,データベースアクセスの結果をテキストで出力している.
使用するHTTPクライアントは,Winsockコントロール版でも,EasySocketコントロール版でも同様の結果を返す.
<% Set dbConnection = Server.CreateObject("ADODB.Connection")
dbConnection.Open "BIBLIO"
Set rs = dbConnection.Execute("SELECT * FROM Authors where Au_ID < 100")
Do While Not rs.EOF
Response.Write(rs("Au_ID") & "," & rs("Author") & "," & _
rs("Year Born") & Chr(13) & Chr(10))
rs.MoveNext
Loop
%>
|
ASPファイル「aspdb.asp」の解説
図4:Active Server Pagesを使ってHTTPクライアントからデータベースアクセス![]() |
Active Server Pagesでは,ひとつのファイルの中で,HTMLコードとASPスクリプトが混在する.そのため,これらを区別する符号が必要になる.リストの最初と最後にある<% 〜 %>がASPスクリプトを指定するタグであり,このタグに囲まれた部分がASPスクリプトである.aspdb.aspはHTMLコードを含まないため,すべてのコードが<% 〜 %>タグで囲まれている.
最初の行はADOのConnectionオブジェクトのインスタンスを作成している.
Set dbConnection = _
Server.CreateObject("ADODB.Connection")
ADOは,RDOの次の世代のデータベースアクセスオブジェクトである.それを利用するためにActive Server Pagesのスクリプトで実体化する.この行はActive Server PagesからADOを利用してデータベースをアクセスする場合には,必須の処理となる.
次のコードはデータベースへの接続を確立する.
dbConnection.Open "BIBLIO"
ConnectionオブジェクトのOpenメソッドは,引数で渡された情報をもとにデータベースへの接続を行なう.このコードで指定されている“BIBLIO”はサーバーのODBCで登録されたデータベースソース名である.つまり,このASPファイルを実行するためには,BIBLIOというデータソース名で,Visual Basicに付属のデータベースファイルbiblio.mdbが登録されている必要がある.
データベースにSQLを実行するには,ConnectionオブジェクトのExecuteメソッドを使用する.
Set rs = dbConnection.Execute _
("SELECT * FROM Authors where Au_ID < 100")
このコードによりRecordsetオブジェクト「rs」に,取得した結果セットが格納される.
以下のコードは取得した結果をテキストとして出力するためのものである.結果セットを先頭から最終行まで参照するためにループを利用している.ループの書式やRecordsetオブジェクトのMove...メソッドの使用法はVisual Basicと同じなので,馴染みのある方も多いだろう.
Do While Not rs.EOF
Response.Write _
(rs("Au_ID") & "," & _
rs("Author") & "," & _
rs("Year Born") & Chr(13) & Chr(10))
rs.MoveNext
Loop
ResponseオブジェクトのWriteメソッドは,引数で渡された内容を出力する.出力テキスト最後にChr(13) & Chr(10)を付加しているのは,改行を行なうためである.Webブラウザに結果を返す場合には, Chr(13) & Chr(10)の代わりに改行を指示するHTMLタグである
を使用する.
| HTTPクライアントで指定した任意のSQL文を実行する |
|---|
図5:クライアントからSQLを指定して実行![]() |
前出の例ではサーバー側に保存されたSQLをHTTPクライアントからのリクエストをトリガにして実行した.これはストアドプロシージャと同等の動作である.サーバー側に配置したロジックを,クライアントからの要求で実行する.しかし,データベースサーバーはやはり,クライアントで指定したSQL文も実行できた方がよい.
それを実現するためには,リクエストするASPファィルをリスト4のaspdb2.aspに変更する.
実行した画面が図5である.リクエスト入力のテキストボックスを拡大した以外は,前出のものと同じである.次のようなリクエストが,実行されていることがテキストボックスの内容からわかる.
aspdb2.asp?SQLreq=SELECT+* +FROM+Authors +where+Au_ID+<+100
?(クエスチョン)以降で指定されているSQLreqが引数としてaspdb2.aspに渡される.引数SQLreqの内容であるSQL文の空白を,+(プラス)に置き換えていることに注意してほしい.この置き換えはプログラムではなく,人為的に行なっている.
このリクエストに対応して,SQLを受け取れるように変更したのは,ASPファイル中の次の行である.
Set rs = dbConnection.Execute _
(Request("SQLreq"))
Requestオブジェクトを使用してクライアントから受けとったSQL文をスクリプト内で展開している.この時点で,+に置き換えて指定したSQL文は正常にデコードされる.
ループによるレコードセットからデータを取り出す部分を変更しているのは,可変数のフィールドに対応するためのものであり,データアクセスには直接関係がない.
<% Set dbConnection = Server.CreateObject("ADODB.Connection")
dbConnection.Open "BIBLIO"
Set rs = dbConnection.Execute(Request("SQLreq"))
Do While Not rs.EOF
FieldssCount = rs.Fields.Count
i = 0
Do While i < FieldssCount
Response.Write(rs.Fields(i) & " ")
i = i + 1
Loop
Response.Write(Chr(13) & Chr(10))
rs.MoveNext
Loop
%>
|
| まとめ |
|---|
今回は寄り道をしてHTTPクライアントから,ASP利用してデータベースアクセスする方法について説明した.少し特殊な印象を持つかもしれないが,決してトリッキーな方法ではない.この方法を使えば,余計なプログラミングをすることなく,Internetベースのデータベースアプリケーションが作成できる.とくにActiveX DocumentsのデータベースアプリケーションをInternetを経由して使用したい場合に適しているだろう.
技術的なメリットとしては,サーバー側が完全にマルチスレッドアプリケーションとして動作することが挙げられる.リクエストを受けたWebサーバーは,各接続をスレッドに分割してデータベースアクセスに利用する.マルチユーザーの使用時にも非同期で動作する上,シングルプロセスのマルチスレッドアプリケーションとして動作するため,不要なメモリの消費もない.しかし,Jetデータベースエンジンを使用する上では,シングルプロセスのマルチスレッドは必ずしも最適な方法だというわけではない.
また,Active Server Pagesのセッションが,疑似セッションだということも忘れてはならない.HTTPクライアントやWebサーバーを作成するとわかるように,HTTPは一連のセッションを消化するとサーバー側で強制的に切断する.Active Server Pagesはページ間のセッションをサポートするが,それはサーバー側に独自のメモリ空間を用意して待機しているに過ぎない.セッションの維持方法として,この方法に問題があるわけではない.問題なのは本当のセッションがどのタイミングで終了したのかがわからないため,タイムアウトの概念が持ち込まれることである.ユーザーは次に再びアクセスしてくるかもしれないし,アクセスしないかもしれない.だから,一定時間が経過したら,もうアクセスはないとみなすのである.
これはクライアント側からみれば,ひとつの賭けになる.情報の取得後,ユーザーはデータ編集し,更新情報をサーバーに送信しようとする.編集作業に何分の時間がかかったにしろ,送信先のサーバー側ではタイムアウトが発生している可能性がある.もちろん,クリティカルな処理では,賭けをすることは許されない.編集データを送信するのが取得直後だとしても,タイムアウトをハンドリングするための処理を記述しなければならない.
もちろん,これには対策がある.最初からセッションに依存しないプログラミングを行なうのである.私はActive Server Pagesでプログラミングする場合には,常にこの方法を採用する.しかしこれは誰にでもすすめられるというわけではない.
このあたりを割り切れるかどうかが,Active Server Pagesを採用するか否かの判断材料になる.これはActive Server Pagesの制限ではなく,Webアプリケーションの制限である.しかし,この連載はWebアプリケーションのためのものではない.
それでは次号以降は,冒頭に述べたデータベースサーバーType2〜4の設計コンセプトと,その実装方法について解説する.