2012年9月5日水曜日

だれでも分かるAJAXとJSONPの仕組みと解説

ウェブブラウザのアドレス欄にウェブページのURLを入力すると、該当ファイルがダウンロードされる。さらにHTML中に記述された画像等のファイルがあれば、すべてダウンロードされる。すべてダウンロードすると、ページの読み込みが完了となる。これが普通のウェブページの動作だ。
完全にダウンロードしてページを表示してしまった後に、ページ中の何かをクリックする等のイベントで、サーバーから別の情報をダウンロードしてきて、ページの表示へ反映させるには、非同期通信を使う。
非同期通信はJAVA Scriptを使うことで可能になる。現在のインタラクティブなウェブサイトにおいては常識的な技術のひとつと言えるだろう。
単にクリックで情報を切り替えて表示するだけの、例えばタブ切り替え機能であれば、サーバーから非同期で情報を取得せずとも、最初にHTMLページをロードしたときに、すべてのタブページの情報を含んだHTMLページを取得しておいて、イベントの発生時に表示内容を切り替えるようにすればよいことだ。だが、タブボタンをクリックしたタイミングで、はじめてサーバーから取得するには、非同期通信を使う。
タブ切り替えのような、些細な機能であれば、非同期通信を使って遠回りなことをしなくてもいいことが多いのだが、もう少し高度なことをしはじめると、やはり非同期通信が必要になってくる。

非同期通信は、任意のタイミングでウェブブラウザからサーバーへリクエストを投げて、サーバーからレスポンスを返す仕組みが必要になる。これにはPHP、CGI+Perl、Ruby等に代表されるサーバーサイドプログラミングが求められる。
先ほどのタブページの例で言えば、ウェブブラウザが何番のページの情報が欲しいというリクエストをサーバーへ送信し、サーバーが該当するページの情報をデータベースやファイルから取得して、ウェブブラウザへ返すようなイメージとなる。

ウェブブラウザ側の非同期通信は、AJAXとJSONPという二つの方法がある。どちらもJAVA Scriptで実現される。

・AJAXとは
AJAXは、XMLHttpRequest()関数やActiveXObject()関数を用いて、サーバーと通信する方法である。その実はただのHTTP通信にすぎない。サーバー側ファイルへGETまたはPOSTのリクエストとして、通信が行われる。ちなみに、ソケット通信をするわけではない。
リクエスト時のパラメータはURLパラメータの形式で行う。具体的には「para1=abc&para2=def」のようにだ。これであれば、サーバー側のプログラムはGETやPOSTとして普通にパラメータを受け取れる。サーバー側は受け取った情報に基づいて、通常のHTTP出力と同様にレスポンステキストを作成して返す。
ウェブブラウザは受け取ったレスポンステキストをXML形式とみなして処理することもできるし、テキストとみなして処理することもできる。
XMLというのはタグでくくるデータのフォーマットだが、そのパース(解析)はウェブブラウザに用意されたJAVA Scriptの関数を利用するだけでできる。

var value = xhr.responseXML.getElementsByTagName("response")[0].firstChild.nodeValue;

このようにするとXMLの中からresponseという名称のタグのひとつの値を取得できる。
だが、見ての通りたったひとつの値を取り出すだけなのに長ったらしい。XMLは階層構造と配列を持つので、複雑な記述になるのだと思われる。
コードの記述を短くするために、テンポラリ変数に移し替えながら使うこともできるが、そのようにしなければならないこと自体がまどろっこしい。
XMLは以下のようなタグでくくられる形式だ。

<DATA1>abc</DATA1>
<DATA2>123</DATA2>

タグなので、ひとつのデータごとに、前後に同じタグ名を書く。たとえば<DATA1>と書いて、終端に</DATA1>と記述するが、同じようなことを2つ書くので冗長だ。abcという3バイトをレスポンスするために、前後のタグ名に16バイト費やす。これひとつのことであれば、問題ないが、数千個の配列で同じ事だとすると、転送量はユーザーの体感にも影響がでかねない。
同じタグ名を前後に書くから不経済なのは分かっている。終端は終わりであることが分かればよいのだから、単なる記号一つにでも置き換えれば、かなり削減できるのだが、そうなっていないものは仕方ない。
また、タグと同じ文字列をデータ中に含めることはできない。

<DATA1>ab</DATA1>c</DATA1>

"ab</DATA1>c" という文字列をDATA1としているつもりであるが、うまくいかない。不等号記号等をエスケープして回避する必要がある。

もうそれなら、レスポンスにXMLを使うのはあきらめて、もうひとつのテキスト形式を選ぶのはよい方法だ。テキストは、文字通りただのテキストなので、そのままウェブブラウザに表示するような用途にも使えるし、CSV等の独自フォーマットでレスポンスを受け取ることに使うこともできる。だが、実際はテキストがJSONと呼ばれる形式になっているとみなして取り扱われることが少なくない。

・JSON形式とは
JAVA Scriptで配列変数を初期化するには、以下のように記述する。

var a1 = [123, 'ABC', 3.14];

純粋なJAVA Scriptのコードである。これで、a1[1]には文字列"ABC"が設定される。
連想配列の書き方は、以下のようになる。

var a2 = {'name':'Taro', 'age':13, 'address':'Hokkaido'};

a2['age']またはa2.ageには13という値がセットされている。
JAVA Scriptで、[]は配列、{}は連想配列のことだ。
配列と連想配列を組み合わせたり、階層構造も記述できる。

var a3 = {'name':'Taro', 'age':13, 'address':['Hokkaido', 'Sapporoshi', 'Kitaku']};

a3['address'][2]には"Kitaku"が入っている。

JAVA Scriptにはeval()という関数がある。引数に文字列を指定すると、それを式として評価した結果を返す。

var a = eval("3+4");

これは変数aに7が入る。
これを応用すると

var a1 = eval("[123, 'ABC', 3.14]");

のように書ける。文字列"[123, 'ABC', 3.14]"が式として評価され、変数a1へ代入される。この文字列は配列の初期化式であるから、a1は配列で初期化される。
このような配列のフォーマットをした文字列をJSON形式と呼ぶ。

このJSON形式をAJAXのレスポンスとして活用できる。
XMLでレスポンスするのと比較して、バイト数は圧倒的に少なくなる。エスケープするのも'または"だけで良い。
なにより受け取ったウェブブラウザ側はeval()関数でもとに戻すだけで、後は普通の配列変数としてレスポンスデータにアクセスできる。

・JSONPとは
非同期通信については、以上AJAXとJSONを使えば実現できるが、ひとつだけどうしてもできないことがある。
HTMLをダウンロードしたドメイン以外にはAJAX通信できないということだ。
http://aaa.com/1.htmlのJAVA Scriptからは、http://bbb.com/2.phpにAJAX通信できない。これはクロスサイトスクリプティングというセキュリティ上の制約に引っかかるからだ。この制約を回避する方法としてJSONPがある。JSONにP(Padding)をつけてJSONPである。
セキュリティと言えば、何か脆弱性があるのではないかと気になるかもしれないが、分かって使う分には問題ない。

AJAXによる他サイトへのアクセスはできないわけだが、他の代替方法として、例えば<IMG>タグであればできる。

<IMG SRC="http://bbb.com/image.jpeg">

これは普通に可能だ。画像ファイルimage.jpegを他サイトbbb.comからダウンロードして表示できる。
となれば、

<IMG SRC="http://bbb.com/s.php?p1=abc&p2=def">

とも書けるので、他サイトへリクエストパラメータを送信できる。
<IMG>タグをJAVA Script内で動的に生成すれば、他サイトから画像をロードするふりをして、任意のタイミングで他サイトにパラメータ送信することが可能になる。
しかし、リクエストを送信できてもレスポンスを受け取ることはできない。
ならば、<SCRIPT>タグを使う。

<SCRIPT SRC="http://bbb.com/s.php?p1=abc&p2=def"></SCRIPT>

これは<IMG>タグと同じアイディアで、パラメータを送信している。
<SCRIPT>タグは元来、外部JAVA Scriptを読み込むためのものである。C言語でいうところのinclude文に相当する。通常は<HEAD>タグの中に記述し、HTMLファイルがダウンロードされたとき、最初に一回実行されるものだ。そのためダウンロードされたHTML中にあるJAVA Script内から動的に<SCRIPT>タグを生成することができない。
しかし、インラインフレームは動的に生成できる。インラインフレームを動的に作り、その中に動的にドキュメントを書き込むことは可能だ。そのドキュメントに<SCRIPT>タグとソースファイルが記述されていたなら、ウェブブラウザはインラインフレーム内のドキュメントを表示するために、<SCRIPT>タグに書かれたソースファイルをダウンんロードする。インラインフレームは動的に任意のタイミングで生成できるので、<SCRIPT>タグでも、任意のタイミングで通信させることができることになる。その際にインラインフレームを非表示で行えば、ユーザーから一部始終も見えない。

以下のような文字列をインラインフレームにドキュメントとして流し込めば、

<SCRIPT SRC="http://bbb.com/s.php?p1=abc&p2=def"></SCRIPT>

サーバーのs.phpにはGETリクエストが送信され、パラメータとしてp1=abcとp2=defが渡される。受け取ったs.phpはそのパラメータを処理し、ウェブブラウザにレスポンスをテキストとして送信する。
ウェブブラウザはレスポンステキストがJAVA Scriptだと思っている。<SCRIPT>タグのSRCに書かれたファイルだからだ。
ウェブブラウザはそのテキストをJAVA Scriptとして実行しようとする。
それが以下のようなテキストだとすると、

callback("{'name':'Taro', 'age',:13}");

これはcallback()という関数を"{'name':'Taro', 'age',:13}"という文字列を引数にして呼び出していることになる。
以下のように、インラインフレームに流しこむドキュメントにcallback()関数を用意しておけば、それがコールされることになるだろう。

<SCRIPT SRC="http://bbb.com/s.php?p1=abc&p2=def"></SCRIPT>
<SCRIPT>
 function callback(x)
 {
 }
</SCRIPT>

あとは、この関数が受け取った引数を親フレームに渡せば、最終的に親フレーム側でレスポンスを受け取れたことになる。
このように<SCRIPT>タグを応用(悪用?駆使?)することで、パラメータの送信と、レスポンスの受信が実現可能だ。

・JSONPはクロスサイトスクリプティングも可能なので、それができないAJAXは必要ない?
JSONPでできるクロスサイトスクリプティングをAJAXはできない。大は小を兼ねるということで、できる方のJSONPを使っておけばいいのだろうか。
まず、JSONPはGETのみ可能で、POSTでリクエストできない。それなら、GETしか使わないという手もある。
それでも、私はJSONPより、できればAJAXの方がいいのではないかと思う。
その理由は、ご覧の通りJSONPは、やり方が少々強引だ。動的にインラインフレームを生成し、ウェブブラウザにJAVA Scriptを実行させているものだと誤解させている。特に二回、同じリクエストをすると、ウェブブラウザはキャッシュを使おうとすることがある。ウェブブラウザはJAVA Scriptだと思っているので、ロードされるスクリプトがそうそう改変されると思っていないからだ。リクエストに時刻や乱数等のダミーパラメータを付加することで、ファイル名が毎回変わるようにして、キャッシュさせないようにすることはできる。
しかし、私はJSONPの仕組みが泥臭い感じがして、できれば避けたい気持ちがある。実感としてもJSONPはまれにうまく動かないことを経験している。これは再現性に確証があるわけではないし、ウェブブラウザによって安定性が異なる可能性がある。実は私のプログラムの問題だったのかもしれない。だから断言できないが、私はJSONPを100%信用していない。

Yahoo!やGoogleに、JSONPで情報を取得できるサービスがある。そのようなものを使うときにJSONPを使うのはよい方法だが、通常はAJAXを使うのが良いのではないかと思っている。
AJAX+JSONがベスト。別ドメインにアクセスするときにJSONPを使うとよいだろう。

新入社員や未経験者のプログラミング言語の習得方法

コンピュータソフトウェアを作成するには、通常、プログラミングという作業を行う。コンピュータソフトウェアの開発には設計やテスト等もあるのだが、実際にプログラムを作る工程はプログラミングと呼ばれる。
プログラミングとはプログラムを書くことであり、そのプログラムには様々な種類がある。例えば、C++やJAVA、PHP等と呼ばれる。
プログラミングは、そうしたプログラミング言語を使える人が行うのだが、その習得方法について、勘違いをしている人がいるようだ。

昔、マトリックスという映画があった。近未来を描いたSF映画だ。
その中で、何度か登場したシーンなのだが、これはSFだなあと思えるものがある。
頭にヘッドギアのようなものをつけて、CD-ROMをセットし、スイッチポンすると、あっという間に特殊スキルが身につくのだ。
これを使うと、カンフーとかヘリコプターの操縦方法を数秒で習得できる。パソコンソフトのように、いろんな種類のスキルを収録したCD-ROMが存在する設定で、それを次々と脳にインプットしていけば、どんどんスキルが身につく。
現実は、格闘技であれば筋力トレーニングが必要であるし、スキルは経験をもとに身につくものなので、脳に何か入れて済むものではないが、映画の世界なのでなんでもありだ。

しかし、これと似たような期待をする人が現実にもいる。
資格試験もそのような性格のものの一種だ。資格試験に合格しているからといって、想定されるスキルがあるかどうかは分からない。その可能性を当然のものとして意識しておかないと、資格試験の合格者を誤って見てしまうことがある。資格試験を完全に意味のないものととらえることもないが、能力判定には限界があることは理解しておかなければならない。

プログラミング言語の習得についても、同じような勘違いをしている場合がある。
特定の手順。具体的に言うと講習を受けたり勉強をすればプログラミング能力が身につくと考える人がいる。
ある会社では、勉強会のようなものを開催して、プログラミング能力がほとんどない人にプログラミングを習得させようとしていたりしていた。私はこれは効果的な方法に思えない。
なぜなら、プログラミングは修練だからだ。別の言い方をすれば筋力トレーニングなのだ。
実際に繰り返しプログラミングを経験することで技能は定着する。失敗や間違いを繰り返して成長し、その人なりのプログラミングスタイルが確立していく。

プログラムを書いた後、コンパイルとかビルドという処理を通す必要のあるプログラミング言語があるのだが、プログラムを少し書いた後、コンパイルを行うと、コンパイラから必ずエラーとしてプログラムの記述間違いが指摘される。プログラムの記述を間違えさえしなければ、何もエラーにならないのだが、人間というものは絶対に間違いを犯すので、99.999%エラーを吐く。さらにコンパイルやビルドの処理を終えても、いざ実行してみれば、一度目は、まず思った結果にならない。何度もトライアンドエラーして、徐々にまともに動くようになる。
プログラミング作業は理屈や計算通りにいかないのだ。間違えずに書けばコンパイルエラーにならない。だけど、実際は間違える。なら、間違えずに書けと言っても、現実は絶対に間違えるのだ。間違えずに書けば、エラーはでないはずだし、書いた本人もわざと間違えては書かない。だが、現実はほぼ確実に間違えて書いている箇所がある。コンパイルエラーにならないように間違えずに書けというのは現実には無理難題だ。理想通りにはならない。そのような現実はコンパイルエラーのことだけに限らない。

プログラムを100人に書かせると、よっぼど単純なプログラムを厳密な規約で書かせないかぎりは、100通りのプログラムが作成される。
決まったやり方なんてものはなく、同じ事をあらゆる方法で記述できる。
プログラムのベターな記述方法についての議論はあるだろうが、そんなことにこだわっていてはいつまで経って製品はできあがらない。プログラムの記載方法に関する機微にいつまでもこだわっている必要はなく、よほど冗長とかということでもなければ、常識的に支障がないのであれば問題ないのだ。
コンピュータープログラミングは工業製品のように同じものを量産するものではない。創造的な作業だ。だから、定型的なプロセスで能力が身につけられるという、わかりやすい話ではない。

プログラミングの講習を行っても、すべての事を網羅することはできない。
ファイルの取り扱い方法やネットワーク、オブジェクト指向プログラミングの基礎的なことを学習することはできるが、今後必要になるだろう、あらゆるすべてのことを網羅することは不可能だ。それに使いもしないものは忘れるに決まっている。必要としていない暗号のようなものを丸暗記ではそれほど詰め込めない。
知らないことは、必要になったときに書籍やインターネットで調べたり、ソースコードを解析したり、場合によっては自分で調査、試行錯誤して理解するものだ。そのように自分で調べるから身につくし、どのみち現実のプログラミング作業はそういうものだ。

なにより一番言いたいのは、必要ないことは習得できないし、必要なことは自然に習得するということ。必要ないことを学習しても、使わなければ忘れてしまうので、無駄に終わる。
だから、私はプログラミングのもっとも最適な習得方法というのは、実際にプログラミングを行うことだと思う。
プログラミングできない人にプログラミングさせるというのは、鶏が先か、卵が先か、みたいな話で、荒療治のように聞こえるが、例えば現存するプログラムの一部を修正させるようなことからはじめて、徐々に規模の大きなものをやらせることは可能だ。
痛みやかゆみを感じずに、楽してなにかを習得することはできない。簡単に習得できるものなら、そんなものに価値はない。プログラミングはそれなりに価値の認められる技能ではあるが、それは誰でもが短時間でたやすくできることではないからだ。
長い目で見て、たくさん痛い思いをしながらでなければ、血や肉にならない。

講習や勉強会のようなものを経て、プログラミング技能者が生産されるというような発想は、マトリックスのスイッチポンと同じようなもので、ないものねだりのありえない希望だ。
講習や勉強といった眠たいことをさせるのは、本人の意欲を失わせるばかりで時間の無駄にしかならない。実際の業務などの意味あるプログラミングをさせる中で習得させたほうが、本気で取り組むため、圧倒的に学習効果が高い。
ミスされることを恐れて、現実のプログラミング業務に従事させないのもいいが、いつまでも無駄な時間を費やしているわけにもいかないわけで、結局は業務にあたらせて、たくさん失敗しながら成長してもらうよりしかたないし、そこからが本当の学習になる。

銀の弾丸はない。

新しくなったロゼッタストーンでスタジオスケジュールが表示されず予約できない

先日、ロゼッタストーンがリニューアルバージョンアップしましたが、トラブルらしきものが起こっているようです。
スタジオスケジュールの画面が正常に表示されないことがあります。画面が出ないのでスタジオの予約ができません。
いつまでも通信中を示す輪っかが回っている状態で、スタジオスケジュールが表示されません。
この画面で止まったままになる

正常な場合は、このように表示されます。
正常な時の表示

この画面が出せないと、スタジオの予約ができないので、スタジオに参加できません。
いつもこのようになるのではなく、ときどきこの状態になり、数時間に渡ってスタジオ予約画面が出なくなります。
新機能の画面下にある英語チャットでも、ときどきこの話題が出ていますので、多くの人がそのような経験をしているようです。

以下、少々、技術的な話になりますが、
まず、画面でグルグル回っているものは単なるアニメーションGIF画像かなにかですから、通信状態をつぶさに表すものではありません。AJAX通信等でユーザーを待たせるときに、「サボっているのでなく、なんかやってますよ~」というパフォーマンスを見せるために、よくこのような表示を使います。
実際の通信状態を調べてみましたところ、おかしくなっているときであってもスケジュール用のデータファイルは正常にダウンロードされています。ダウンロードは6、7秒で終わっています。その後はグルグル画像が回っているだけで、一生懸命なにかやっているふりをしていますが、裏で何かしているわけではありません。ぼーっとしてるだけですので、いつまで待っていても無駄です。
ダウンロードされているデータファイルの中身は一見正常なJSON形式に見えます。その詳細までは解析していません。

以上のことから考えられる原因として、次のようなことが推測ができます。
1.ダウンロードしたファイルのフォーマットが壊れている。
2.ダウンロードしたファイルをクライアントプログラムが処理できない。

1の場合、サーバーサイドの問題ですので、サーバー側での対応が必要となります。
いつでもスケジュール画面が出ないわけではないので、特定の条件で作成されるデータファイルが正常な形式で作成されていないのかもしれません。
あるいは、サーバー側システムになんらかのトラブルがあって、正常なデータファイルが作成されないのかもしれません。

2の場合、クライアントプログラムのアップデートが必要になるかもしれません。
クライアントプログラムがアップデートされれば、自動配布されるでしょうから、それで解決するのではないかと思います。

コンピュータソフトウェアに不具合は付き物です。動作テストはしているでしょうが、想定していない思いがけない問題があったのだと思います。
ユーザー側の立場としては納得いかないものがあるかもしれませんが、「あることは証明できるが、ないことは証明できない」という、論理的に誰にもどうしようもない摂理があります。
コンピュータソフトウェアを作ったとしても、バグがあることを証明することはできますが、バグがないことを証明することはできません。ですので、通常、コンピュータソフトウェアのテスト過程においては、不具合が枯れてきて、統計的に不具合が残存する可能性が限りなく低下したことをもってテストが完了したと見なします。これは、「まあもうバグはないだろう」と妥協するものであって、不具合が確実に0になったことを証明するものではありませんし、誰にもできません。
それ以降、不幸なことに不具合が発見されたときは、そのときに、最善の対処をするしかありません。
だからといって、コンピュータソフトウェアの不具合を容認しては、それに甘えてテストを十分に行わないということが起こりますので、問題が起こったなら起こったで、そのときは責任を負うことが求められます。

リニューアルなんかしなくても、安定した以前のままでいいという意見もあるかもしれませんが、それは既存ユーザーの視点であって、ロゼッタストーン社も競争の中で商売をしているのですから、ずっと同じ場所にとどまっているわけにはいきません。他者の追撃への警戒がありますし、成長させないソフトウェアでは、社内的なモチベーションも維持できません。ある程度の周期で、リニューアルを繰り返すことで、営業における宣伝材料にもなるでしょう。
ならば、それはロゼッタストーン社の都合であって、既存ユーザーはそのようなことに関心がないということも言われるでしょう。
リニューアルするなら、既存ユーザーにもメリットがある形で行わないと理解を得られにくくなるということだと思います。

このスタジオ予約画面が出せないというのは、そこそこ大きな問題ですので、すぐに対処されるでしょうし、現状、このようになったときはしばらく待ってみるしかありません。
予約さえできれば、スタジオの参加自体は正常にできています。
今は改善されるのを待ちましょう。