プログラミング講義の演習でペアプログラミング

けっこう長い年月、大学で文系の学生を中心に初心者向けのプログラミングの講義とExcelを中心としたアプリケーション利用の講義を受け持っている。プログラミングの方は以前は自分の書籍、そして改訂が追いつかなくなってからはその内容を書き換えて作ったテキストで教えている。ずっとJavaを教えているがJava言語を教えるのは学校の方針なので変えられない。ほとんどの学生は、おそらくエンジニアにならないと思うし、ここまでにプログラミングについての何かの講義を受けたことは前提にしていない。プログラマ養成という目的はなく、「世の中にはこういう世界もあるものだ」と思ってくれればいいくらいの講義であり、以前は受講者はすごく少なかった。もう1つ講義を持っていて、そちらはExcelを中心にしているが、数年前まではExcelの講義の方はまだ受講者は多かった。しかし、なぜかちょっとその傾向が変わっている。比較的まじめにJavaの勉強をする学生が増えて来た。情報処理試験を受ける人がその学部でも多いということを別の先生からも聞くが、もしかしたらスマホブームみたいなこともあるのかもしれない。

毎年同じように淡々とやってきたのだが、数年前にExcelの講義よりもJavaの講義を最後まで受ける人の方が多くなってきて、ちょっとやり方を変えるべきかと思うようになってきた。少しレベルを上げて、繰り返し基本をやるのをやめたりとかしてきたが、今年から「ペアプログラミング」を演習に取り入れるようにしようとした。講義は半分は解説しながらプログラムを打ち込んだりして、残りの半分は演習に充てている。去年までは基本的に1人ずつでやっていた演習を今年はペアプログラミングにしようとした。しかし、いきなり最初から人と組むのはどうかと思い当初は1人での演習とした。つまり、ベースになる知識がいくらかでも浸透しないと会話が成り立たないと考えたのである。今日は4回目の講義で、今回の演習でペアプログラミングやってみたのだが、結論を言うと正解だった。学生の表情がきわめて明るく、また、相談しながら何かをやることでの達成感があるのか、「できたー」と声を出す学生は1人や2人ではなかった。記録のため、その状況をブログに残すことにする。

  • 先週までで基本型を説明した。今日は文字列についての章で、+、length、substring、そしてStringBufferのinsert、appendくらいしか教えてない。それらでできるプログラミングとしては基本的なレベルの演習を与えた。
  • ペアプログラミングという言葉を使ったが、アジャイルとは何かみたいな話は一切していない。説明したのは「1台のパソコンを代わる代わる使って演習をやりなさい」という程度、また、適当に打ち込む側を代わることと、横から見ている人は「言葉で茶々を入れる」ということを強調した。問題ごとに代わるペアもあったが、「随時必要に応じて代わる」点を強調しておいたので、主体的に関わる側に随時切り替わるペアもあった。
  • チームをどう作るか迷ったが、講義は学科や学年をまたぐものであって、あまり同一学科の同一学年が集まっておらず、学生同志はほとんど面識のない様子だったので、単純に座席の近い者でペアを組ませた。人数が奇数だったのだが、1名のみTAが茶々入れ役をした。
  • ほぼ全員が、明るい表情で演習を続け、また、お互いに話をすることで、講義にありがちな重苦しい雰囲気は一気に吹っ飛んだ。通常の演習よりもリラックスができたのだと思われる。また、演習問題が出来上がった事は声や表情等に出るのだが、一人でやるよりも達成感があったのではないかと想像できる。また、問題の解釈をディスカッションしたり、うまく行かないポイントを会話するのを聞く事で、どういうところを迷っているのかもある程度は観察できる状態だった。
  • 問題に関する質問がほぼなかった。通常は演習中に、TAが走り回ってサポートしているが、サポートの必要がなかった。つまり、2人で演習にかかれば疑問点は解決できるということのようだ。
  • 終わってから作ったプロジェクトを作業していないPCでログインしている人に渡すように指導をしたが、そこで悩むことの方が多かった。この点についてはあまり指導しないでおいたので、仕方ない面はある。たぶん、次回からUSBメモリが必要だと気付く人もいただろう。また、下手なことをするよりもプログラムをメールで送り合った方が楽という点も気付くペアはいた。いずれにしても、ここは随時指導を加えるポイントである。

そういうわけで、ペアプログラミングってどうだろうかと慎重にスタートしてみたが、たぶん、これは学生も教師もやりやすい手法のようだ。特に、学生同志で、それぞれ上下関係が薄いという場合には問題なくワークしそうだ。学年の違いもまあ、あの年齢になるとそんなに気にならないのだろうか。ペアを今後どのように作るかちょっと悩ましいところもあるが、固定化しないでやるのがいいのか、あるいは固定化させるべきか考えないといけない。だが、いずれにしても、ペアプログラミングによる演習は積極的に取り入れて良さそうだ。

Byte誌を創刊した人が亡くなられたという記事を読み…

PragPubの最新号(Oct 2013)を読んでいたら最後のページに訃報が掲載されていた。Wayne Green、1975年にBytes誌を創刊、いっしょにビジネスをしていた元妻と即トラブって後々の同誌の栄光時代はともに歩んでいないとはいえ、PragPubはMr.Greenの最大の功績としてByteの創刊を挙げている。日本だと、私と同世代を中心に「日経バイト」の読者も多いだろうし、その隣の編集部で私は何年か過ごしただけに関係者とも今でもFacebookでつながっている。日本で発行する遥か前に関わった人ではあるけど、こうして雑誌で紹介されるのは、Byte誌の存在感はすでに廃刊している今も大きいということだ。

日本で日経バイトが発行されたのは80年代、私は高校→大学→就職→転職とめまぐるしい10年だったが、コンピュータ雑誌は情報を得る最も先端的な手段だと皆が思っていた。研究室では当然ながら誰かが買って来た1冊を回し読みをする。みんなお金がないので、特定の雑誌を買っている研究室にわざわざ読みにも行く。そんな研究室で、日経バイト購買担当は自分だった。「日経バイト」を読んでいたら、コンピュータに疎い同級生が「いいの載っているか?」と聞いてくる(そのバイトやない…)。「すごいよー」と言って見せると、ひー!と言いやがった。その日経バイトの翻訳記事でMacintoshはよく掲載されていた。毎日自分が見ているコンピュータのあまりの違いにショックであり、すごく高価だけどアメリカではこんな先を走っているのかと印象付けられた。そして現在がある。

出版や雑誌の存在感が大きな時代は、もはやなつかしいものになった。そんな時代に短いながらも出版社で雑誌を作る仕事ができたことは、得難い経験になった。自分のキャリアのベースはそこにある。雑誌は特にチームプレイであり、「映画を作るような熱狂」(映画を作った事はないので想像だけど)を年がら年中やっていたような高揚感があったことを思い出す。時代は移り、インターネットの発展でメディアの立ち位置は大きく変わったが、Byte誌という言葉を聞くとなんだかワクワク感が戻ってくると同時に、はるか昔のさまざまなことを思い出させてくれる。Mr.Green、享年91歳。

FileMaker ServerのAdmin Console起動のファイルの入手すらできない場合

OS XでFileMaker ServerのAdmin Console接続の続報です。なんとかうまくセットアップができたとしても、そのうちあれこれやっていると、16000番ポートにつなぐとこれになることがあります。もちろん、Javaのインストールはすでにやってあってもこれが出てしまうとします。そうなると、admin_console_webstart.jnlpというファイルのダウンロードができません。

スクリーンショット 2013-09-28 0.44.01

そのようなときには、ブラウザのアドレス欄に以下のようなURLを入れます。これで、admin_console_webstart.jnlpが入手できます。

http://ホスト名:16000/admin_console_webstart.jnlp

OS XでFileMaker ServerのAdmin Console接続

JavaVMをAppleがリリースしなくなり、OS XのJavaは混沌としてしまっていますが、FileMaker Serverを使うにはどうしても避けて通れません。FMS自体を動かすためのJavaのバージョンについてはFileMakerのサイトにいろいろな情報が掲載されていますが、サーバ管理ツールのAdmin Consoleについてもバージョンの混在で訳が分からなくなりつつあります。

OS Xのメンテナンスをどうやってきたかによって多分動作は違うと思いますが、私のメインマシンは以下のようにどうしようもない状態になりました。

  • ログイン時に作ったアプリケーションでは起動しなくなった。
  • 16000番ポートに接続して表示されるページのアイコンをクリックしても、Admin Consoleは表示されなくなった。admin_console_webstart.jnlpがダウンロードされるだけ。
  • サーバからadmin_console_webstart.jnlpというファイルがダウンロードできれば、それを右クリックして、「Java Web Start」を選択すると、なんとか起動できるようになった。

3つ目の方法でなんとか凌ぐということもできるのですが、なんか不便です。また、admin_console_webstart.jnlpに接続先情報が組み込まれているためと思われますが、サーバごとにkのファイルを残しておかないといけません。そんなわけで、コマンドラインで管理したりとか、だんだんダークな世界に入りつつあり、なんとかできないのかと思ったのですが、Automaterでアプリケーションを作ればいいじゃんということに気付きました。こんなのを作ります。

fig2

このワークフローは、Automaterで新しくウインドウを作ったときに「アプリケーション」を選択します。最初の「指定されたFinder項目を取得」は、admin_console_webstart.jnlpファイルをウインドウ内にドラッグ&ドロップすれば作成できます。ダウンロードしたadmin_console_webstart.jnlpファイルは、私の場合いくつもあるので適当に名前を変えています。

そして、続くワークフローでは「Finder項目を開く」で、「このアプリケーションで開く」で「Java Web Start」を指定します。ただ、ポップアップニューには「Java Web Start」は出てきません。ポップアップメニューで、「その他」を選択してファイル選択のダイアログボックスを表示します。そして、command+shift+Gを押して、移動先のパスを入力するシートを表示して、「/System/Library/CoreServices」に移動します。そこには、「Java Web Start」のアプリケーションがあるので選択ができます。

こうしてアプリケーション自体をサーバごとに作っておけば、ダブルクリックでAdmin Consoleを起動することができます。

[FMRP] 繰り返しフィールド、ポータル、ポータルフィルタ

しばらくさぼってしまっていましたが、FileMaker Relationship Patternsの続きです。

カテゴリ:1対多

名前:繰り返しフィールド

問題:

1エンティティに対して複数のエンティティが存在する場合に、複数のエンティティを一覧表示したい

解決策:

繰り返しフィールドを必要な数、つまり複数のエンティティに対応するフィールドの数だけ定義して、レイアウト上に配置する

フォース:

TO間の結合を定義するのではなく、フィールドのオプション設定として、繰り返す数の上限を1より大きな値に設定する事で、繰り返しフィールドを用意する

繰り返しフィールドで複数のレコードに対する保存は確保できるが、厳密な意味でのテーブル間結合ではなく、第一正規形も満たしていない点を考慮する必要がある

複数エンティティを起点としたリレーションシップは確保できず、集計処理などができない。たとえば、伝票で明細を繰り返しフィールドで作れば「伝票」という帳票は作成できるが、明細行のレコードに関して商品ごとの売り上げ集計はできなくなる。

繰り返し数の上限は、データベースの定義で決める必要がある。繰り返し数が後から足りなくなった場合に増やす事もできるが、データベースの設計変更を伴う

繰り返しフィールドはスクロールができないため、レコード数が多数になると、レイアウトの広い範囲を繰り返しフィールドが占めてしまい、使い勝手が悪いレイアウトができてしまう

関連パターン:

ポータル


カテゴリ:1対多

名前:ポータル

問題:

1エンティティに対して複数のエンティティが存在する場合に、複数のエンティティを一覧表示したい

解決策:

レイアウト上にポータルを配置して、レイアウトのTOとリレーションシップでつながっている別のTOを割り当てる。これにより、レイアウトのTOのレコードと対応関係にあるポータルのTOのレコードが、ポータルに一覧される

フォース:

典型的な1対多の展開である。リレーションで直接つながっているTOだけでなく、複数のTOを経由した先のTOでもポータルには配置できる。

ポータルの中にポータルは配置できない。ポータルの中では1対1のリレーションシップの先の単一のフィールドしか配置できない。

関連パターン:

ポータルフィルタ、繰り返しフィールド


カテゴリ:1対多

名前:ポータルフィルタ

問題:

ポータルに表示するレコードを条件に応じて制限したいが、制約をリレーションシップで定義したくない

解決策:

ポータルフィルタの設定を行う

フォース:

FileMakerのリレーションシップの制約の1つは、複数のTOが存在するTOGにおいて、すでに2つ先になっているTOと直接結合することができないことである。つまり、条件の設定をSQLのように柔軟にできないため、リレーションシップだけで必要なレコードに絞り込むことが困難な場合も出てくる。ポータルフィルタは単にレコードに式をあてはめて、表示するかどうかを判定するだけである。ただし、式に使えるフィールドはリレーションシップに影響する可能性が発生するが、1つの解決方法はグローバルフィールドを用いて条件を与え、式で判定させるということができる点がある。従って、レイアウトに表示するという状況では、リレーション以外に制約を与える方法が存在するため、すべてをリレーションで解決する必要はない。

フィルタにより一部のレコードだけが表示されるが、フィルタの式の結果がすべてtrueつまり、フィルタ前のすべてのレコードを一度は読み込むため、レイアウトを表示した最初のときに非常に時間がかかるケースも発生する。FIleMaker 12の段階では、サーバサイドでフィルタを適用しているのではなく、クライアントサイドで適用していることが原因と考えられる。

関連パターン:

ポータル

INTER-Mediatorのターゲット指定を汎用化する

INTER-Mediatorでのターゲット指定は、タグ要素のclass属性に、IM[table@field@target] といった形式で記述するものだ。これにより、tableで指定したコンテキストにあるfieldというフィールドのデータを、そのタグ要素に埋め込む。targetを省略すると、フォーム要素以外ではテキストノードとして追加し、フォーム要素ではvalueやcheckedなどの適切な属性に設定される。targetでは、Ver.3.8現在、属性名、innerHTML、nodeText、$target、#target、style.styleNameをサポートしている。

このところ、いろいろなものをINTER-Mediatorで作っていて、ある同じようなパタンが頻繁に発生していることに気付いた。それは、

あるフィールドのデータがあれば表示し、なければ表示しない

という動作である。稀に逆もあるのだが、基本は同じだ。これを実現するために、たとえば、FileMakerだと、計算フィールドを利用する。あるフィールドfieldに対して、計算フィールドdisplayField = if ( field != “”; “block” ; “none” )を定義する。そしてたとえば、こんな感じのHTMLを書く。

<div class=”IM[table@displayField@style.display]”>
データ:<span class=”IM[table@field]”></span>
</div>

SQLデータベースなら、ビューを利用するのがいいだろう。たとえば、こんな感じだろうか?

CREATE VIEW tableview AS
SELECT field, if(LENGTH(field)>0, ‘block’, ‘none’) AS displayField FROM table;

これで対処はできるとは言え、もう少し簡単にならないかと考えてしまう。こうしたよくあることを手軽に対処できるというのはフレームワークに要求されることだ。もちろん、1つの方法は、ターゲット指定の3つ目のパラメータを増やす事だ。たとえば、こんなのはどうだろう?

<div>
データ:<span class=”IM[table@field|table@field@parentVisibility]”></span>
</div>

parentVisibilityという名前の属性はないので、HTMLのルールとのコンフリクトはない。フィールドの値が “” あるいは長さが0であれば、visibilityだったら自分自身、parentVisibilityだったら1レベル上位のノードのdisplayスタイルをnoneにするというところだ。

ということで、機能を増やしました…というのはなんかこの段階に来てやることかなと疑問に考えた。もっと汎用的にすべきではないのか?

そこで考えたのが、次のような仕様である。tagetの代わりに、プログラムのステートメントのような書き方をするということ。たぶん、これがいちばん汎用的と思われる。

table@field@object.method(parameters)

だが、いきなりこれだけですというのは敷居を高くするだけじゃないのかと思われるだろう。もちろん、その解決策はある。object.method(parameters)に対するエイリアスを定義することで、従来通りの記述をサポートするようにする。つまり、…@$onclickや、…@innerHTMLという記述はサポートする。ただし、これらはエイリアスという位置づけにする。ただ、内部的には#や$の扱いが微妙だが、これはif文をがんばって書くしかないだろう。

objectは、次の書き方をサポートしようと考えている。(self) (parent) (enclosure) (repeater) つまり、カッコの中に決められたキーワードを記述する。methodとparametersは任意だ。ただ、parametersは(“a”,’b’)なんて書いたときにカッコ内をそのまま渡すのはセキュリティ上の問題が出そうな感じありありであるので、ここは制約を付けることにする。

ターゲットの3つ目に記述する処理のためのオブジェクトINTERMediatorTargetを作っておく。たとえば、そこに、次のように、setInnerHTMLメソッドがあるとしよう。ここでの関数の最初の引数は、フィールドのデータであるというルールを適用することにする。

INTERMediatorTarget {
setInnerHTML: function(d) {
this.innerHTML = d;
}
}

ターゲット指定は table@field@(self).setInnerHTML() と記述する。そして、[(self).setInnerHTML()」のエイリアスが「innerHTML」であるというわけである。

ノード展開の処理では、ターゲット指定のあるノードnodeに対して、その指定に従ったフィールドのデータfieldDataが得られている。ということは、以下のようにapplyを使えばいいということになる。これは、INTER-Mediator内部の話であって、アプリケーションを作る側は気にしなくてもいい。

INTERMediatorTarget.setInnerHTML.apply( node, [fieldData] );

このノードに展開する仕上げに相当する値をセットする部分が現状ではひどくifの応酬となっているので、まずはそれを緩和したい。また、上記のような仕組みだと、INTERMediatorTargetオブジェクトへのメソッド追加や既存メソッドの書き換えにより、フレームワークの動作を改変することも可能だし、独自のターゲット指定記述を作る事もできる。

INTERMediatorTargetに記述する関数では、前記のように第1引数にフィールド値を設定するのはいいとして、汎用性を高めるために、2、3引数はフィールド名とコンテキスト名にする。これで、コンテキストやフィールドに応じて動作を変える事もできてします。

こういう実装を考えているところである。

[IM]MediaAccessクラスと新たな拡張点(3)

こちらの文書、さらにこちらの文書の続きです。

データ生成を指定したクラスにさせ生成結果を返す

URLを使用して、別のシステムにデータを取りに行くことができるということはかなり汎用的になります。しかしながら、別システムとの連動ということになり、開発はやや大変になると同時にセキュリティ面への配慮する場面も増えることになります。そこで、INTER-Mediatorで完結させるために、定義ファイルの呼び出しにおいて、

context.php?media=class://ClassName/path/info

という呼び出しができるようになりました。この場合、ClassNameは定義ファイルと同じディレクトリなど、PHPが取得できる場所にあれば問題ありません。パス情報を付与できますが、mediaキーの値を利用することができるのです。

ClassNameで指定したクラスには、processingというメソッドを記述します。そして、ヘッダを含めた応答すべてをこのメソッドの中で完結させます。

class ClassName {
function processing($contextData, $options) { }
}

スペック上はprocessingというメソッドがあればいいのですが、引数については、とりあえず作ったアプリケーションで必要なものを並べました。$contextDataは、アクセス権の設定と同じ情報をパスに持たせることで、そのレコードのフィールドとデータがキーと値になった配列を設定します。つまり、関連付けられているレコードのデータをそのまま使えるようになっています。現状は、認証の設定がなされていないとこの変数には値は設定されません。$optionは、定義ファイルのIM_Entryの2つ目の値が設定されます。なお、メソッドの引数は今後は増えると思われます。

この仕組みを作って作ったのが『FileMaker as a Relational Database』のサイトです。このサイトでは、書籍を購入した人にPDFおよびePubでの書籍を配布していますが、それぞれパーソナライズをしています。PDFはヘッダに購入者やメールアドレスを入れ込み、ePubでは特定のページに同じような内容のテキストを埋め込んで圧縮・アーカイブします。media=class::…という記述は、そうしたドキュメント生成処理を記述できるような仕組みなのです。

MediaAccessクラスの解説は以上で終了です。

[IM]MediaAccessクラスと新たな拡張点(2)

こちらの文書の続きです。

FileMakerのオブジェクトフィールドを参照する

オブジェクトフィールドはデータベースごとに扱いが異なり、統一的にはやりにくい処理ではあります。MediaAccessクラスでは、FileMaker Serverをターゲットにオブジェクトフィールドに対応する機能を作成しました。

FIleMakerはFXを経由し、XML共有の仕組みを使います。テキスト型や数値型は、基本的にテキストでフィールドにあるデータが得られます。一方、オブジェクトフィールドはオブジェクトそのもののデータではなく、フィールドのデータに応じた以下のような「テキスト」が得られます。

/fmi/xml/cnt/photo.jpg
http://server:16000/…

PDFは完全なURLで、FileMaker Serverに16000ポートで接続して取り出すことが可能です。JPEGなどの画像の場合は、URLのパスに相当するものが得られますが、たとえばIMGタグのSRC属性にそのまま指定が可能な文字列になります。

いずれにしても、両方ともURLであると解釈すればいいわけです。この仕組みはFileMaker Serverに限らず、一般的なアクセスにも使えます。つまり、media=URLと指定をした場合は、そのURLにアクセスしたデータをMediaAccessクラスが取得し、さらにクラスを呼び出した元にそのデータを返します。/fmi/xml/cntで始まる物だけは特別にURLであるという処理が組み込まれています。また、URLかどうかはそれ以外には、httpあるいはhttpsで開始するものかどうかで判定しています。

MediaAccessでのアクセス権

認証が成立したらすべて参照可能となり、成立しなければ参照不可という単純なものなら非常に話が早く、前に説明したmedia-tokenの仕組みで事は足りるのです。なお、データはCRUDの4つの側面がありますが、メディアに関しては「編集」というのはWeb世界ではかなり難易度が高い世界であり、MediaAccessクラスはほぼ参照のみのサポートになっています。

ここで、メディアそのものがログインしたユーザごとのアクセス権を持たせたい場合が出てきます。レコードについては、特定のフィールドにユーザ名やグループ名を入力することで、そのユーザやグループに所属したユーザでないと参照や更新ができない仕組みを持っています。フィールドのデータはそれでいいのですが、メディアは実体はレコードと別に有ります。ここで、個々のメディアをコンテキストのレコードと結合させ、レコードごとのアクセス権をメディアにまで及ぼす仕組みを組み込みました。

まず、IM_Entry関数の2つ目の引数(オプション引数)に、キーが’media-context’で値がコンテキスト名(コンテキストのnameキーに対する値)を与えます。すると、メディアはこのコンテキストにある特定のレコードの、1つのフィールドのようなふるまいになります。

‘media-context’ => ‘context-name’,

実際にメディアにアクセスするパスは次のような形式にします。つまり、コンテキスト名、レコードの検索条件をパスに入れます。最初のfilesは特に意味はなく、相対パスの最初のキーワードです。context-nameは、media-contextの値と同じでなければなりません。そのコンテキストのkeyキーに対する値、つまり主キーがpidであるなら、たとえば、field=valueは、pid=312 のような値になります。

context.php?media=files/context-name/field=value/filename.png

ここでもし、media-root-dirが /var/www/media であるなら、実際に

/var/www/media/files/context-name/pid=312/finename.png

という絶対パスの画像ファイルが存在する必要があります。コンテキストの定義には、レコード単位のアクセス権設定があれば、メディアに対するアクセス権の認証の確認を行い、ユーザを特定します。pid=312のレコードがそのユーザにアクセス権があるのかどうかを確認することによって、アクセスの可否を決めます。従って、適当にpid=316などとパスを変えても、その条件で検索されたレコードが他のユーザに対する権利があるものであれば、400番台のレスポンスを返してデータは返しません。認証の確認を行い、そのユーザに対するアクセス権がないものは出力しないという仕様によりアクセス権は定義した通りに適用されます。

pid=312を、age=45のようにできると言えばできますが、おそらく、そういうディレクトリは存在せず、漏洩にはならないでしょう。

ファイルのアップロードのコンポーネントは、コンテキスト名やレコード検索条件のパスを自動的に作成するようにも作られています。

とまあ、ここまで書いたところで、ソースに間違いを発見! また、グループ名をフィールドに入れる場合はまだ実装していなかったことも思い出しました。今のところ、メディア関連処理は、開発している側で「必要の合った」状況しかうまく動かない可能性があります。ぜひとも、いろいろ試してフィードバックをください。

(まだ続く)

[IM]MediaAccessクラスと新たな拡張点

INTER-Mediatorはデータベースの内容を取り出し、Webページに展開し、場合によっては書き直したデータをデータベースに書き戻します。この一連のDBを中心としたデータの流れがあるのですが、Webアプリケーションではこれだけではすべてはまかなえません。HTMLでページを作るときは写真などの画像を別のファイルで供給します。このHTML外に実データが存在するようなものを「メディア」と呼ぶことにします。

メディアにはいろいろな種類があります。主要なものはIMGタグ要素で表示する画像、OBJECTタグなどで表示するFlashのコンテンツやビデオなど、そしてリンク先で得られるものとなるでしょうか。また、それぞれ、サーバ上にスタティックにあるものや、データベースのオブジェクト型フィールドに存在する場合もあります。WebページからこれらにアクセスするためのものがMediaAccessクラスです。概ね、次のような用途に使用方法は分類できます。

  • サーバ上にある画像ファイルを参照する
  • FileMakerのオブジェクトフィールドを参照する
  • データ生成を指定したクラスにさせる、生成結果を返す

ここで、単なる画像は普通にHTMLを書けばいいじゃないかと思うかもしれませんが、MediaAccessクラスを使う理由は、認証やアクセス権の設定との連携が可能になっているところです。

サーバ上にある画像ファイルを参照する

スタティックな画像で、認証が絡まない場合は、普通にIMGタグを記述します。また、画像ファイル名やパスがデータベースにある場合でも、必ずしもMediaAccessクラスは必要ないかもしれません。たとえば、あるテーブルのfilepathフィールドにファイル名が記録されているのなら、このようなIMGタグで表示できるでしょう。#srcにより、この要素のSRC属性にフィールドのデータが追加されます。

<img src=”figs/” class=”IM[context@filepath@#src]” />

ページの内容を認証しなければ参照できないようにしたとき、やはりページに埋め込んだ画像なども認証を経由したいと考えます。Webサーバレベルでの認証の場合は、ある意味、認証がない場合と概ね同じことで可能ですが、INTER-Mediatorの認証機能を使った場合、SRC要素がスタティックな画像を参照していれば、もしかしたら、認証しなくても画像だけは見えてしまうかもしれません。そんなことをしても大した情報流出にならないとも言えるのですが、ガードしたいものはガードするのが基本ですし、認証しているのに一部は誰でも見えるのは正しい運用ではありません。

そこで、IM_Entry関数の2つ目の引数(つまりオプション引数)に、’media-root-dir’というキーで、サーバ上にあるメディアファイルのフォルダへのルートからの絶対パスを指定しておきます。たとえば、

‘media-root-dir’ => ‘/var/www/images’,

となっていたとします。あるページファイルで使われている定義ファイルのパスがcontext.phpだったとします。すると、次の部分URLが、メディアを返します。ここで背後では、MediaAccessクラスが使われており、このクラスがメディアに対するプロキシになっているとも言えます。

context.php?media=ch1/shot345.png

たとえば、IMGタグのSRC属性に上記のURLを記述すれば、media-root-dirと合成して、「/var/www/images/ch1/shot345.png」というファイルをアクセスし、その内容を取り出して、適切なMIMEタイプのヘッダとともに出力をします。

前記のURLにある画像ファイル名「shot345.png」がフィールドpicfileにあるような場合には、ページファイルの要素には以下のような記述ができます。media=は決められたキーワードです。

<img src=”context.php?media=ch1/” class=”IM[table@picfile@#src]” />

このとき、定義ファイルで認証が必要な設定になっていると、SRC属性のURLからの取得時にMediaAccessクラス内部で、認証の確認を行います。ここで、一般の認証時に使っているクレデンシャルをそのまま使うことが実はできません。一般の認証では、1つのアクセスごとに異なるチャレンジデータを使うことで、認証の乗っ取りをしにくくしています。しかし、そのためにメディア処理ではその仕組みが使えません。なぜなら、メディアへのアクセスは並列的にブラウザから行うからです。

そこで、メディアアクセス時の認証のためだけのクレデンシャルを生成させるようにしています。たとえば、直前のimgタグ要素の場合、tableという名前のコンテキストからの取得となりますが、そのコンテキスト定義(IM_Entry関数の第1引数)の’authentication’キー内部の’media-handling’キーの値をtrueにします。すると、このコンテキストのデータを取得するときに、サーバ側からメディア用のクレデンシャルを出力します。

IMGタグ要素などから実際にメディア取得を定義ファイルに対して行うとき、media=があれば、MediaAccessクラスに処理をまかせます。そのとき、クレデンシャルがクッキーに記録されてサーバ側に到達し、それを発行したクレデンシャルと比較することで認証が通っているかどうかを判定します。つまり、コンテキストが得られるということは認証が通っているわけで、その信頼関係をもとに秘密の合い言葉をやりとりします。このクレデンシャルは繰り返し使われる可能性があります。クッキーに記録し、時間が来れば消去するようにはなっているものの、クレデンシャルを盗まれるのはそれだけでメディアアクセスを可能にしてしまうことになります。従って、HTTPS(SSL)でのサーバ運用は必須とも言えるでしょう。

普通のHTMLでMediaAccessを経由させる

Webページを作って画像を埋め込むと、<img src=”img/cover.jpg” /> などと記述します。これをMediaAccessクラスを使ってデータの取り出しを行いたいとしたら、ソースを全部変更しないといけないのかというとそうではありません。リダイレクトを使うことで、HTMLソースはそのままに、MediaAccessクラスを使うことができます。たとえば、.htaccessファイルを作り、たとえば次のような記述を作ります。

RedirectMatch img/(.+) http://host.name/myapp/context.php?media=$1

すると、<img src=”img/cover.jpg” /> は、<img src=”http://host.name/myapp/context.php?media=cover.jpg” /> と同じことになるわけです。

部分パスと絶対パス、それらを消したり追加したりといろいろ複雑にはなりますが、このスタティックなメディアを認証した上で表示できるようにしたのがMediaAccessの最初のインプリメントでした。

(この内容、続く)